Installation
Added by DRT 2021-12-03
Updated by DRT 2023-11-29
source("getReqdPkgs.r")
pkgs <- c("SingleCellExperiment","scater","scran","Seurat","miloR",
"tidyverse","patchwork","igraph","ggplot2",
"ggrastr","msigdbr","clusterProfiler",
"RColorBrewer","cowplot","devtools")
extra_pkgs <- c("irlba","Matrix") # NOTE these must be installed from source to make code work
invisible(getReqdPkgs(pkgs))
'getOption("repos")' replaces Bioconductor standard repositories, see 'help("repositories", package = "BiocManager")' for
details.
Replacement repositories:
CRAN: https://cran.rstudio.com/
Warning: downloaded length 0 != reported length 0Warning: cannot open URL 'https://bioconductor.org/packages/3.17/bioc/src/contrib/PACKAGES.rds': HTTP status was '404 Not Found'All required CRAN and BioC packages have previously been installed
NOTE
As of 2023-11-29, the runPCA function in scater breaks due to change
in Matrix library; must downgrade to Matrix 1.6.1 Downloaded v 1.6.1
from https://cran.r-project.org/src/contrib/Archive/Matrix/
See this link: https://github.com/bwlewis/irlba/issues/70
# install.packages("Matrix", type = "source")
# install.packages("irlba", type = "source")
library(SingleCellExperiment)
Loading required package: SummarizedExperiment
Loading required package: MatrixGenerics
Loading required package: matrixStats
Attaching package: ‘MatrixGenerics’
The following objects are masked from ‘package:matrixStats’:
colAlls, colAnyNAs, colAnys, colAvgsPerRowSet, colCollapse, colCounts, colCummaxs, colCummins,
colCumprods, colCumsums, colDiffs, colIQRDiffs, colIQRs, colLogSumExps, colMadDiffs, colMads, colMaxs,
colMeans2, colMedians, colMins, colOrderStats, colProds, colQuantiles, colRanges, colRanks, colSdDiffs,
colSds, colSums2, colTabulates, colVarDiffs, colVars, colWeightedMads, colWeightedMeans,
colWeightedMedians, colWeightedSds, colWeightedVars, rowAlls, rowAnyNAs, rowAnys, rowAvgsPerColSet,
rowCollapse, rowCounts, rowCummaxs, rowCummins, rowCumprods, rowCumsums, rowDiffs, rowIQRDiffs, rowIQRs,
rowLogSumExps, rowMadDiffs, rowMads, rowMaxs, rowMeans2, rowMedians, rowMins, rowOrderStats, rowProds,
rowQuantiles, rowRanges, rowRanks, rowSdDiffs, rowSds, rowSums2, rowTabulates, rowVarDiffs, rowVars,
rowWeightedMads, rowWeightedMeans, rowWeightedMedians, rowWeightedSds, rowWeightedVars
Loading required package: GenomicRanges
Loading required package: stats4
Loading required package: BiocGenerics
Attaching package: ‘BiocGenerics’
The following objects are masked from ‘package:stats’:
IQR, mad, sd, var, xtabs
The following objects are masked from ‘package:base’:
anyDuplicated, aperm, append, as.data.frame, basename, cbind, colnames, dirname, do.call, duplicated,
eval, evalq, Filter, Find, get, grep, grepl, intersect, is.unsorted, lapply, Map, mapply, match, mget,
order, paste, pmax, pmax.int, pmin, pmin.int, Position, rank, rbind, Reduce, rownames, sapply, setdiff,
sort, table, tapply, union, unique, unsplit, which.max, which.min
Loading required package: S4Vectors
Attaching package: ‘S4Vectors’
The following object is masked from ‘package:utils’:
findMatches
The following objects are masked from ‘package:base’:
expand.grid, I, unname
Loading required package: IRanges
Loading required package: GenomeInfoDb
Loading required package: Biobase
Welcome to Bioconductor
Vignettes contain introductory material; view with 'browseVignettes()'. To cite Bioconductor, see
'citation("Biobase")', and for packages 'citation("pkgname")'.
Attaching package: ‘Biobase’
The following object is masked from ‘package:MatrixGenerics’:
rowMedians
The following objects are masked from ‘package:matrixStats’:
anyMissing, rowMedians
library(scater)
Loading required package: scuttle
Loading required package: ggplot2
library(scran)
library(miloR)
Loading required package: edgeR
Loading required package: limma
Attaching package: ‘limma’
The following object is masked from ‘package:scater’:
plotMDS
The following object is masked from ‘package:BiocGenerics’:
plotMA
Attaching package: ‘edgeR’
The following object is masked from ‘package:SingleCellExperiment’:
cpm
library(tidyverse)
── Attaching core tidyverse packages ────────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.1.4 ✔ readr 2.1.4
✔ forcats 1.0.0 ✔ stringr 1.5.1
✔ lubridate 1.9.3 ✔ tibble 3.2.1
✔ purrr 1.0.2 ✔ tidyr 1.3.0── Conflicts ──────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ lubridate::%within%() masks IRanges::%within%()
✖ dplyr::collapse() masks IRanges::collapse()
✖ dplyr::combine() masks Biobase::combine(), BiocGenerics::combine()
✖ dplyr::count() masks matrixStats::count()
✖ dplyr::desc() masks IRanges::desc()
✖ tidyr::expand() masks S4Vectors::expand()
✖ dplyr::filter() masks stats::filter()
✖ dplyr::first() masks S4Vectors::first()
✖ dplyr::lag() masks stats::lag()
✖ ggplot2::Position() masks BiocGenerics::Position(), base::Position()
✖ purrr::reduce() masks GenomicRanges::reduce(), IRanges::reduce()
✖ dplyr::rename() masks S4Vectors::rename()
✖ lubridate::second() masks S4Vectors::second()
✖ lubridate::second<-() masks S4Vectors::second<-()
✖ dplyr::slice() masks IRanges::slice()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(patchwork)
library(igraph)
Attaching package: ‘igraph’
The following objects are masked from ‘package:lubridate’:
%--%, union
The following objects are masked from ‘package:dplyr’:
as_data_frame, groups, union
The following objects are masked from ‘package:purrr’:
compose, simplify
The following object is masked from ‘package:tidyr’:
crossing
The following object is masked from ‘package:tibble’:
as_data_frame
The following object is masked from ‘package:miloR’:
graph
The following object is masked from ‘package:scater’:
normalize
The following object is masked from ‘package:GenomicRanges’:
union
The following object is masked from ‘package:IRanges’:
union
The following object is masked from ‘package:S4Vectors’:
union
The following objects are masked from ‘package:BiocGenerics’:
normalize, path, union
The following objects are masked from ‘package:stats’:
decompose, spectrum
The following object is masked from ‘package:base’:
union
library(ggplot2)
library(ggrastr)
library(msigdbr)
library(cowplot)
Attaching package: ‘cowplot’
The following object is masked from ‘package:patchwork’:
align_plots
The following object is masked from ‘package:lubridate’:
stamp
library(Matrix)
Attaching package: ‘Matrix’
The following objects are masked from ‘package:tidyr’:
expand, pack, unpack
The following object is masked from ‘package:S4Vectors’:
expand
library(irlba)
library(RColorBrewer)
Differential Abundance analysis with Milo
We test for differential abundance between healthy and cirrhotic
livers. We start by defining neighbourhoods with refined sampling on the
KNN graph. We inspect the size of neighbourhoods.
liver_milo <- Milo(liver_sce)
## Build KNN graph
liver_milo <- buildGraph(liver_milo, d = 11, k=30)
Constructing kNN graph with k:30
## Compute neighbourhoods with refined sampling
liver_milo <- makeNhoods(liver_milo, k=30, d=11, prop = 0.05, refined=TRUE)
Checking valid object
Running refined sampling with reduced_dim
plotNhoodSizeHist(liver_milo, bins=150)

Then we make a design matrix for the differential test, assigning
samples to biological conditions.
colData(liver_milo)[['sort']] <- str_remove(colData(liver_milo)[['dataset']], ".+_")
colData(liver_milo)[['sort']] <- str_remove(colData(liver_milo)[['sort']], "A|B")
liver_meta <- as_tibble(colData(liver_milo)[,c("dataset","condition", 'sort')])
liver_meta <- distinct(liver_meta) %>%
mutate(condition=factor(condition, levels=c("Uninjured", "Cirrhotic"))) %>%
column_to_rownames("dataset")
Now we can count cells in neighbourhoods and perform the DA test.
liver_milo <- countCells(liver_milo, samples = "dataset", meta.data = data.frame(colData(liver_milo)[,c("dataset","condition",'sort')]) )
Checking meta.data validity
Counting cells in neighbourhoods
liver_milo <- calcNhoodDistance(liver_milo, d=11)
'as(<dgTMatrix>, "dgCMatrix")' is deprecated.
Use 'as(., "CsparseMatrix")' instead.
See help("Deprecated") and help("Matrix-deprecated").
milo_res <- testNhoods(liver_milo, design = ~ condition, design.df = liver_meta[colnames(nhoodCounts(liver_milo)),])
Using TMM normalisation
Performing spatial FDR correction withk-distance weighting
milo_res_sort <- testNhoods(liver_milo, design = ~ sort + condition, design.df =
liver_meta[colnames(nhoodCounts(liver_milo)),])
Using TMM normalisation
Performing spatial FDR correction withk-distance weighting
compare_da_df <- left_join(milo_res_sort, milo_res, by="Nhood", suffix=c("_sort", "_nosort")) %>%
{annotateNhoods(liver_milo, ., 'annotation_lineage')}
compare_da_df %>%
ggplot(aes(-log10(SpatialFDR_sort), -log10(SpatialFDR_nosort))) +
geom_point(size=0.8) +
geom_point(data=. %>% filter(annotation_lineage=="Endothelia"), color="red")

plot(milo_res_sort$SpatialFDR, milo_res$SpatialFDR)

Exploration of Milo DA results
We can start by looking at some basic stats
pval_hist <- milo_res %>%
ggplot(aes(PValue)) +
geom_histogram(bins=50) +
theme_bw(base_size=14)
volcano <-
milo_res %>%
ggplot(aes(logFC, -log10(SpatialFDR))) +
geom_point(size=0.4, alpha=0.2) +
geom_hline(yintercept = -log10(0.1)) +
xlab("log-Fold Change") +
theme_bw(base_size=14)
pval_hist + volcano

The distribution of P-values looks sensible and from the volcano plot
we can see that we have identified some DA neighbourhoods at 10%
FDR.
We can visualize DA neighbourhoods building an abstracted graph
liver_milo <- buildNhoodGraph(liver_milo)
plotNhoodGraphDA(liver_milo, milo_res, alpha = 0.1, size_range=c(2,6))

Note: This step is not required. It generates a large file (even
larger than raw data) and then reloads it.
## Save milo object and results
# saveRDS(liver_milo,"~/dropbox-vu/temp/milo_data/Ramachandran2019_liver/liver_milo_20210225.RDS")
# write_csv(milo_res,"~/dropbox-vu/temp/milo_data/Ramachandran2019_liver/liver_results_20210225.csv")
# liver_milo <- readRDS("~/liver_milo_20201008.RDS")
liver_milo <- readRDS(url("https://www.dropbox.com/s/xdp07789c5hoen3/liver_milo_20210225.RDS?dl=1"))
# milo_res <- read_csv("/nfs/team205/ed6/data/Ramachandran2019_liver/liver_results_20201008.csv")
milo_res <- read_csv(url("https://www.dropbox.com/s/i1l3aep1py5wirf/liver_results_20210225.csv?dl=1"))
NOTE: hvgs (presumably highly variable genes) cannot be
loaded since file not provided
## Load hvgs
# hvgs <- scan("~/data/Ramachandran2019_liver/liver_milo_hvgs.txt", "")
Attempting to generate a set of highly variable genes
#
Making figures for the manuscript
colourCount = length(unique(liver_milo$annotation_lineage))
getPalette = colorRampPalette(brewer.pal(9, "Set2"))
Warning: n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
umap_df <- data.frame(reducedDim(liver_milo, "UMAP"))
colnames(umap_df) <- c("UMAP_1", "UMAP_2")
umap1 <- cbind(umap_df, annotation_lineage=liver_milo$annotation_lineage) %>%
ggplot(aes(UMAP_1, UMAP_2, color=as.character(annotation_lineage))) +
geom_point_rast(size=0.1, alpha=0.5, raster.dpi = 800) +
ggrepel::geom_text_repel(data = . %>%
group_by(annotation_lineage) %>%
summarise(UMAP_1=mean(UMAP_1), UMAP_2=mean(UMAP_2)),
aes(label=annotation_lineage), color="black", size=6
) +
scale_color_manual(values=getPalette(colourCount)) +
guides(color="none") +
xlab("UMAP1") + ylab("UMAP2") +
coord_fixed() +
theme_classic(base_size = 22) +
theme(axis.text = element_blank(), axis.ticks = element_blank())
ggsave("~/dropbox-vu/temp/milo_output/liver_v2/liver_umap1_new.pdf", height = 7, width = 8)
umap2 <-
cbind(umap_df, condition=as.character(liver_milo$condition)) %>%
ggplot(aes(UMAP_1, UMAP_2, color=condition)) +
geom_point_rast(size=0.1, alpha=0.5, raster.dpi = 800) +
scale_color_brewer(palette="Set1", name='') +
xlab("UMAP1") + ylab("UMAP2") +
coord_fixed() +
guides(color='none') +
facet_wrap(condition~., ncol=1) +
theme_nothing(font_size = 22) +
theme(axis.text = element_blank(), axis.ticks = element_blank(), legend.position=c(0.9,0.9),
strip.background = element_rect(color=NA), strip.text = element_text(size=22))
ggsave("~/dropbox-vu/temp/milo_output/liver_v2/liver_umap2_new.pdf", height = 7, width = 8)
nh_graph_pl <- plotNhoodGraphDA(liver_milo, milo_res, alpha = 0.1, size_range=c(1,4)) +
theme(legend.text = element_text(size=20), legend.title = element_text(size=22)) +
coord_fixed()
# nh_graph_pl + ggsave("~/mount/gdrive/milo/Figures/liver_v2/liver_graph.pdf", height = 7, width = 8)
# ggsave("~/dropbox-vu/temp/milo_output/liver_v2/liver_graph.pdf", height = 7, width = 8)
ggsave("~/dropbox-vu/temp/milo_output/liver_v2/liver_graph_new.pdf", height = 7, width = 8)
fig4_top <- (umap1 | umap2 | nh_graph_pl) +
plot_layout(widths = c(3,1,3))
fig4_top

Explore DA neighbourhoods by cell type
Next, we can check the cell types where we observe most differences
between healthy and cirrhotic cells, by taking the most frequent cell
type in each neighbourhood.
milo_res <- milo_res[,!str_detect(colnames(milo_res), "annotation_lineage")]
# Add annotation of most frequent cell type per nhood to milo results table
milo_res <- annotateNhoods(liver_milo, milo_res, "annotation_indepth")
anno_df <- data.frame(liver_milo@colData) %>%
distinct(annotation_lineage, annotation_indepth)
milo_res <- left_join(milo_res, anno_df, by="annotation_indepth")
We first check that neighbourhoods are sufficiently homogeneous
frac_hist <- ggplot(milo_res, aes(annotation_indepth_fraction)) +
geom_histogram(bins=30) +
xlab("Fraction of cells in \nmost abundant cluster") +
ylab("# neighbourhoods") +
theme_bw(base_size=14)
frac_hist

Filter nhoods with homogeneous composition
milo_res$annotation_indepth[milo_res$annotation_indepth_fraction < 0.6] <- NA
milo_res$annotation_lineage[milo_res$annotation_indepth_fraction < 0.6] <- NA
I can recover all the clusters where DA was detected in the original
paper
group.by = "annotation_indepth"
paper_DA <- list(cirrhotic=c("MPs (4)","MPs (5)",
"Endothelia (6)", "Endothelia (7)",
"Mes (3)",
"Tcells (2)",
"Myofibroblasts"
),
healthy=c("MPs (7)",
"Endothelia (1)",
"Tcells (1)", "Tcells (3)","Tcells (1)",
"ILCs (1)"
)
)
expDA_df <- bind_rows(
data.frame(annotation_indepth = paper_DA[["cirrhotic"]], pred_DA="cirrhotic"),
data.frame(annotation_indepth = paper_DA[["healthy"]], pred_DA="healthy")
)
pl1 <- milo_res %>%
left_join(expDA_df) %>%
mutate(is_signif = ifelse(SpatialFDR < 0.1, 1, 0)) %>%
mutate(logFC_color = ifelse(is_signif==1, logFC, NA)) %>%
arrange(annotation_lineage) %>%
mutate(Nhood=factor(Nhood, levels=unique(Nhood))) %>%
filter(!is.na(annotation_lineage)) %>%
ggplot(aes(annotation_indepth, logFC, color=logFC_color)) +
scale_color_gradient2() +
guides(color="none") +
xlab(group.by) + ylab("Log Fold Change") +
ggbeeswarm::geom_quasirandom(alpha=1) +
coord_flip() +
facet_grid(annotation_lineage~., scales="free", space="free") +
theme_bw(base_size=22) +
theme(strip.text.y = element_text(angle=0),
axis.title.y = element_blank(), axis.text.y = element_blank(), axis.ticks.y = element_blank(),
)
pl2 <- milo_res %>%
left_join(expDA_df) %>%
# dplyr::filter(!is.na(pred_DA)) %>%
group_by(annotation_indepth) %>%
summarise(pred_DA=dplyr::first(pred_DA), annotation_lineage=dplyr::first(annotation_lineage)) %>%
mutate(end=ifelse(pred_DA=="healthy", 0, 1),
start=ifelse(pred_DA=="healthy", 1, 0)) %>%
filter(!is.na(annotation_lineage)) %>%
ggplot(aes(annotation_indepth, start, xend = annotation_indepth, yend = end, color=pred_DA)) +
geom_segment(size=1,arrow=arrow(length = unit(0.1, "npc"), type="closed")) +
coord_flip() +
xlab("annotation") +
facet_grid(annotation_lineage~.,
# annotation_lineage~"Ramachandran et al.\nDA predictions",
scales="free", space="free") +
# guides(color="none") +
scale_color_brewer(palette="Set1", direction = -1,
labels=c("enriched in cirrhotic", "enriched in healthy"),
na.translate = F,
name="Ramachandran et al.\nDA predictions") +
guides(color=guide_legend(ncol = 1)) +
theme_bw(base_size=22) +
ylim(-0.1,1.1) +
theme(strip.text.y = element_blank(),strip.text.x = element_text(angle=90),
plot.margin = unit(c(0,0,0,0), "cm"), panel.grid = element_blank(),
axis.title.x = element_blank(), axis.text.x = element_blank(), axis.ticks.x = element_blank(),
legend.position = "bottom")
fig4_bleft <- (pl2 + pl1 +
plot_layout(widths=c(1,10), guides = "collect") & theme(legend.position = 'top', legend.justification = 0))
# ggsave("~/mount/gdrive/milo/Figures/liver_v2/liver_DAcomparison.pdf", width=8, height = 13)
# ggsave("~/dropbox-vu/temp/milo_output/liver_v2/liver_DAcomparison.pdf", width=8, height = 13)
pl2

Close-up on Endothelial lineage
endo_milo <- scater::runUMAP(liver_milo[,liver_milo$annotation_lineage=="Endothelia"], dimred='PCA')
Found more than one class "dist" in cache; using the first, from namespace 'BiocGenerics'
Also defined by ‘spam’
Found more than one class "dist" in cache; using the first, from namespace 'BiocGenerics'
Also defined by ‘spam’
plotUMAP(endo_milo, colour_by = "annotation_indepth")

umap_df <- data.frame(reducedDim(endo_milo, "UMAP"))
colnames(umap_df) <- c("UMAP_1", "UMAP_2")
endo_umap <- cbind(umap_df, condition=endo_milo$condition) %>%
ggplot(aes(UMAP_1, UMAP_2, color=condition)) +
geom_point(size=0.3, alpha=0.5) +
scale_color_brewer(palette="Set1", name='') +
xlab("UMAP1") + ylab("UMAP2") +
coord_fixed() +
guides(color="none") +
facet_wrap(condition~., ncol=1) +
theme_nothing() +
theme(axis.text = element_blank(), axis.ticks = element_blank(), legend.position=c(0.9,0.9),
strip.background = element_rect(color=NA), strip.text = element_text(size=22))
liver_milo2 <- liver_milo
subset.nhoods <- str_detect(milo_res$annotation_indepth, "Endo")
reducedDim(liver_milo2, "UMAP")[colnames(endo_milo),] <- reducedDim(endo_milo, "UMAP")
endo_gr <-
plotNhoodGraphDA(
liver_milo2, milo_res,
subset.nhoods = which(milo_res$annotation_lineage == "Endothelia"),
size_range=c(1,4),
# ) =)[1:(length()-1)],
alpha = 0.1
) +
theme(legend.text = element_text(size=20), legend.title = element_text(size=22))
# liver_milo2 <- liver_milo
# subset.nhoods <- str_detect(milo_res$annotation_indepth, "Endo")
# reducedDim(liver_milo2, "UMAP")[colnames(endo_milo),] <- reducedDim(endo_milo, "UMAP")
# endo_gr_groups <- plotNhoodGroups(liver_milo2, milo_res_endogroups[milo_res_endogroups$annotation_lineage=="Endothelia",],
# show_groups = c("54", "70"),
# size_range=c(1,4),
# subset.nhoods = milo_res_endogroups$annotation_lineage=="Endothelia") +
# scale_fill_manual(values=c("54"=brewer.pal(4, "Spectral")[2], "70"=brewer.pal(4, "Spectral")[3]),
# labels=c("54"="Uninjured group", '70'= "Cirrhotic group"),
# na.value="white",
# name = "Nhood group"
# ) +
# theme(legend.text = element_text(size=20), legend.title = element_text(size=22))
fig4_bright1 <- ((endo_umap + endo_gr ) +
plot_layout(widths = c(1,2),
guides = "collect"
))
fig4_bright1

Close-up on Cholangiocytes
chol_milo <- scater::runUMAP(liver_milo[,liver_milo$annotation_lineage=="Cholangiocytes"], dimred='PCA')
Found more than one class "dist" in cache; using the first, from namespace 'BiocGenerics'
Also defined by ‘spam’
Found more than one class "dist" in cache; using the first, from namespace 'BiocGenerics'
Also defined by ‘spam’
plotUMAP(chol_milo, colour_by = "annotation_indepth")

plotUMAP(chol_milo, colour_by = "percent.mito")

Filter out cells that show contamination from immune cells
(expression of immune markers)
keep <- logcounts(chol_milo)["CD19",] == 0 | logcounts(chol_milo)["MS4A1",] == 0
chol_milo <- chol_milo[,keep]
chol_milo <- scater::runUMAP(chol_milo, dimred='PCA')
Found more than one class "dist" in cache; using the first, from namespace 'BiocGenerics'
Also defined by ‘spam’
Found more than one class "dist" in cache; using the first, from namespace 'BiocGenerics'
Also defined by ‘spam’
plotUMAP(chol_milo, colour_by = "annotation_indepth")

umap_df <- data.frame(reducedDim(chol_milo, "UMAP"))
colnames(umap_df) <- c("UMAP_1", "UMAP_2")
chol_umap <- cbind(umap_df, condition=chol_milo$condition) %>%
ggplot(aes(UMAP_1, UMAP_2, color=condition)) +
geom_point(size=0.3, alpha=0.5) +
scale_color_brewer(palette="Set1", name='') +
xlab("UMAP1") + ylab("UMAP2") +
coord_fixed() +
guides(color="none") +
facet_wrap(condition~., ncol=1) +
theme_nothing() +
theme(axis.text = element_blank(), axis.ticks = element_blank(), legend.position=c(0.9,0.9),
strip.background = element_rect(color=NA), strip.text = element_text(size=22))
chol_umap

liver_milo2 <- liver_milo
subset.nhoods <- milo_res$annotation_lineage=="Cholangiocytes"
reducedDim(liver_milo2, "UMAP")[colnames(chol_milo),] <- reducedDim(chol_milo, "UMAP")
chol_gr <-
plotNhoodGraphDA(
liver_milo2, milo_res,
subset.nhoods = subset.nhoods,
size_range=c(2,5),
# ) =)[1:(length()-1)],
alpha = 0.1
) +
theme(legend.text = element_text(size=22), legend.title = element_text(size=24))
(chol_umap + chol_gr ) +
plot_layout(widths = c(1,2),
guides = "collect"
)

# fig4_bright1 +
# ggsave("~/milo_output/liver_endoGraph.pdf", width=9, height = 5)
# ggsave("~/dropbox-vu/temp/milo_output/liver_endoGraph.pdf", width=9, height = 5)
Differential Gene Expression analysis
In a subset of lineages, we want to test for differential expression
between neighbourhoods enriched in cirrhotic cells and neighbourhoods
enriched
Endothelia
Rebuttal figure showcasing grouping
set.seed(42)
milo_res_endogroups <- groupNhoods(liver_milo, milo_res, max.lfc.delta = 2, overlap = 1)
Found 1385 DA neighbourhoods at FDR 10%
nhoodAdjacency found - using for nhood grouping
p1 <- plotNhoodGroups(liver_milo, milo_res_endogroups,
size_range=c(1,3))
milo_res_endogroups <- annotateNhoods(liver_milo, milo_res_endogroups, 'annotation_lineage')
p2 <- plotDAbeeswarm(milo_res_endogroups, group.by = 'NhoodGroup') +
facet_grid(annotation_lineage~., scales="free", space="free")
Converting group_by to factor...
## Plot expression in T cell neighbourhoods
markers_df <- read_csv("~/mount/gdrive/milo/STable3_Ramachandran.csv")
Error: '~/mount/gdrive/milo/STable3_Ramachandran.csv' does not exist.
Group endothelial cells by logFC and DA results
Calculate marker genes between the two groups
Visualize as volcano
Visualize as heatmap
(gene expression values are scaled between 0 and 1 for each gene)
GO term analysis
LS0tCnRpdGxlOiAiTWlsbzogbGl2ZXIgY2lycmhvc2lzIGFuYWx5c2lzIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKLS0tCgojIyBJbnRyb2R1Y3Rpb24KCkluIHRoaXMgbm90ZWJvb2sgd2UgZGVtb25zdHJhdGUgaG93IHRvIHVzZSBNaWxvIHRvIGRldGVjdCBhYmhlcnJhbnQgY2VsbCBzdGF0ZXMgaW4gZGlzZWFzZWQgdGlzc3VlcywgdXNpbmcgYSBkYXRhc2V0IG9mIGhlcGF0aWMgbm9uLXBhcmVuY2h5bWFsIGNlbGxzIGlzb2xhdGVkIGZyb20gNSBoZWFsdGh5IGFuZCA1IGNpcnJob3RpYyBodW1hbiBsaXZlcnMuIFtSYW1hY2hhbmRyYW4gZXQgYWwuIDIwMTldKGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvczQxNTg2LTAxOS0xNjMxLTMjU2VjMSkgKEdFTyBhY2Nlc3NpaW9uOiBHU0UxMzYxMDMpLgoKIyMgSW5zdGFsbGF0aW9uCiMjIyMgQWRkZWQgYnkgRFJUIDIwMjEtMTItMDMKIyMjIyBVcGRhdGVkIGJ5IERSVCAyMDIzLTExLTI5CmBgYHtyIEluc3RhbGxhdGlvbn0Kc291cmNlKCJnZXRSZXFkUGtncy5yIikKCnBrZ3MgPC0gYygiU2luZ2xlQ2VsbEV4cGVyaW1lbnQiLCJzY2F0ZXIiLCJzY3JhbiIsIlNldXJhdCIsIm1pbG9SIiwKICAidGlkeXZlcnNlIiwicGF0Y2h3b3JrIiwiaWdyYXBoIiwiZ2dwbG90MiIsCiAgImdncmFzdHIiLCJtc2lnZGJyIiwiY2x1c3RlclByb2ZpbGVyIiwKICAiUkNvbG9yQnJld2VyIiwiY293cGxvdCIsImRldnRvb2xzIikKCmV4dHJhX3BrZ3MgPC0gYygiaXJsYmEiLCJNYXRyaXgiKSAjIE5PVEUgdGhlc2UgbXVzdCBiZSBpbnN0YWxsZWQgZnJvbSBzb3VyY2UgdG8gbWFrZSBjb2RlIHdvcms7IHNlZSBuZXh0IGNvZGUgYmxvY2sKCmludmlzaWJsZShnZXRSZXFkUGtncyhwa2dzKSkKYGBgCgoKIyMjIE5PVEUKQXMgb2YgMjAyMy0xMS0yOSwgdGhlIHJ1blBDQSBmdW5jdGlvbiBpbiBzY2F0ZXIgYnJlYWtzIGR1ZSB0byBjaGFuZ2UgaW4gTWF0cml4IGxpYnJhcnk7IG11c3QgZG93bmdyYWRlIHRvIE1hdHJpeCAxLjYuMQpEb3dubG9hZGVkIHYgMS42LjEgZnJvbSBodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9zcmMvY29udHJpYi9BcmNoaXZlL01hdHJpeC8gIAoKU2VlIHRoaXMgbGluazogaHR0cHM6Ly9naXRodWIuY29tL2J3bGV3aXMvaXJsYmEvaXNzdWVzLzcwCgpgYGB7cn0KIyBpbnN0YWxsLnBhY2thZ2VzKCJNYXRyaXgiLCB0eXBlID0gInNvdXJjZSIpCiMgaW5zdGFsbC5wYWNrYWdlcygiaXJsYmEiLCB0eXBlID0gInNvdXJjZSIpCmBgYAoKYGBge3J9CmxpYnJhcnkoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpCmxpYnJhcnkoc2NhdGVyKQpsaWJyYXJ5KHNjcmFuKQpsaWJyYXJ5KG1pbG9SKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoaWdyYXBoKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dyYXN0cikKbGlicmFyeShtc2lnZGJyKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkoTWF0cml4KQpsaWJyYXJ5KGlybGJhKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKYGBgCgoKIyMgTG9hZCBkYXRhCgpXZSBkb3dubG9hZGVkIHRoZSBkYXRhc2V0IGFuZCBhbm5vdGF0aW9ucyBzdG9yZWQgaW4gU2V1cmF0IG9iamVjdCBmcm9tIFtoZXJlXShodHRwczovL2RhdGFzaGFyZS5pcy5lZC5hYy51ay9oYW5kbGUvMTAyODMvMzQzMyksIGFzIGluZGljYXRlZCBieSB0aGUgYXV0aG9ycy4KCmBgYHtyfQojIGxvYWQoIi9uZnMvdGVhbTIwNS9lZDYvZGF0YS9SYW1hY2hhbmRyYW4yMDE5X2xpdmVyL3Rpc3N1ZS5yZGF0YSIpCmxvYWQodXJsKCJodHRwczovL3d3dy5kcm9wYm94LmNvbS9zL2JxODE2aDc0Z21oODRndS90aXNzdWUucmRhdGE/ZGw9MSIpKQojIyBDb252ZXJ0IHRvIFNpbmdsZUNlbGxFeHBlcmltZW50CmxpdmVyX3NjZSA8LSBTaW5nbGVDZWxsRXhwZXJpbWVudChhc3NheSA9IGxpc3QoY291bnRzPXRpc3N1ZUByYXcuZGF0YSwgbG9nY291bnRzPXRpc3N1ZUBkYXRhKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbERhdGEgPSB0aXNzdWVAbWV0YS5kYXRhKQoKbGl2ZXJfc2NlCmBgYAoKIyMgUHJlcHJvY2Vzc2luZwoKV2UgdXNlIHRoZSBzYW1lIG51bWJlciBvZiBoaWdobHkgdmFyaWFibGUgZ2VuZXMgYW5kIHByaW5jaXBhbCBjb21wb25lbnRzIHVzZWQgYnkgdGhlIGF1dGhvcnMgb2YgdGhlIG9yaWdpbmFsIHN0dWR5LiAKCiMjIyBGZWF0dXJlIHNlbGVjdGlvbgoKU2VsZWN0IGhpZ2hseSB2YXJpYWJsZSBnZW5lcwoKYGBge3J9CmRlY19saXZlciA8LSBtb2RlbEdlbmVWYXIobGl2ZXJfc2NlKQoKZml0X2xpdmVyIDwtIG1ldGFkYXRhKGRlY19saXZlcikKcGxvdChmaXRfbGl2ZXIkbWVhbiwgZml0X2xpdmVyJHZhciwgeGxhYj0iTWVhbiBvZiBsb2ctZXhwcmVzc2lvbiIsCiAgICB5bGFiPSJWYXJpYW5jZSBvZiBsb2ctZXhwcmVzc2lvbiIpCgpodmdzIDwtIGdldFRvcEhWR3MoZGVjX2xpdmVyLCBuPTMwMDApCmBgYAoKIyMjIERpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbgoKYGBge3IsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTh9CnNldC5zZWVkKDQyKQpsaXZlcl9zY2UgPC0gcnVuUENBKGxpdmVyX3NjZSwgc3Vic2V0X3Jvdz1odmdzLCBuY29tcG9uZW50cz0xMSkKbGl2ZXJfc2NlIDwtIHJ1blVNQVAobGl2ZXJfc2NlLCBkaW1yZWQ9IlBDQSIsIG5jb21wb25lbnRzPTIpCgpzY2F0ZXI6OnBsb3RVTUFQKGxpdmVyX3NjZSwgY29sb3VyX2J5PSJjb25kaXRpb24iLCBwb2ludF9hbHBoYT0xLCAgcG9pbnRfc2l6ZT0wLjUpCnNjYXRlcjo6cGxvdFVNQVAobGl2ZXJfc2NlLCBjb2xvdXJfYnk9ImRhdGFzZXQiLCBwb2ludF9hbHBoYT0wLjMsICBwb2ludF9zaXplPTAuNSkKc2NhdGVyOjpwbG90VU1BUChsaXZlcl9zY2UsIGNvbG91cl9ieT0iYW5ub3RhdGlvbl9saW5lYWdlIiwgcG9pbnRfYWxwaGE9MC4zLCAgcG9pbnRfc2l6ZT0wLjUsIHRleHRfYnk9J2Fubm90YXRpb25fbGluZWFnZScpCmBgYAoKTm90YWJseSwgdGhpcyBkYXRhc2V0IGRvZXNuJ3QgYXBwZWFyIHRvIGRpc3BsYXkgYSBiYXRjaCBlZmZlY3QKCiMjIyBOb3RlOiBUaGlzIHN0ZXAgaXMgbm90IHJlcXVpcmVkLiBJdCBnZW5lcmF0ZXMgYSBsYXJnZSBmaWxlIChldmVuIGxhcmdlciB0aGFuIHJhdyBkYXRhKSBhbmQgdGhlbiByZWxvYWRzIGl0LiAKYGBge3Igc2F2ZS1sb2FkIGludGVybWVkIGZpbGUgMSwgZXZhbD1GQUxTRSwgaW5jbHVkZT1UUlVFLCBlY2hvPVRSVUV9CiMgc2F2ZVJEUyhsaXZlcl9zY2UsICJ+L2Ryb3Bib3gtdnUvdGVtcC9taWxvX2RhdGEvUmFtYWNoYW5kcmFuMjAxOV9saXZlci9saXZlcl9TQ0VfMjAyMTAyMjUuUkRTIikKIyBsaXZlcl9zY2UgPC0gcmVhZFJEUyh1cmwoImh0dHBzOi8vd3d3LmRyb3Bib3guY29tL3MvejBhb3BmNjE2YjF1cnZqL2xpdmVyX1NDRV8yMDIxMDIyNS5SRFM/ZGw9MSIpKQpgYGAKCiMjIERpZmZlcmVudGlhbCBBYnVuZGFuY2UgYW5hbHlzaXMgd2l0aCBNaWxvCgpXZSB0ZXN0IGZvciBkaWZmZXJlbnRpYWwgYWJ1bmRhbmNlIGJldHdlZW4gaGVhbHRoeSBhbmQgY2lycmhvdGljIGxpdmVycy4gV2Ugc3RhcnQgYnkgZGVmaW5pbmcgbmVpZ2hib3VyaG9vZHMgd2l0aCByZWZpbmVkIHNhbXBsaW5nIG9uIHRoZSBLTk4gZ3JhcGguIFdlIGluc3BlY3QgdGhlIHNpemUgb2YgbmVpZ2hib3VyaG9vZHMuCgpgYGB7cn0KbGl2ZXJfbWlsbyA8LSBNaWxvKGxpdmVyX3NjZSkKCiMjIEJ1aWxkIEtOTiBncmFwaApsaXZlcl9taWxvIDwtIGJ1aWxkR3JhcGgobGl2ZXJfbWlsbywgZCA9IDExLCBrPTMwKQoKIyMgQ29tcHV0ZSBuZWlnaGJvdXJob29kcyB3aXRoIHJlZmluZWQgc2FtcGxpbmcKbGl2ZXJfbWlsbyA8LSBtYWtlTmhvb2RzKGxpdmVyX21pbG8sIGs9MzAsIGQ9MTEsIHByb3AgPSAwLjA1LCByZWZpbmVkPVRSVUUpCnBsb3ROaG9vZFNpemVIaXN0KGxpdmVyX21pbG8sIGJpbnM9MTUwKQpgYGAKClRoZW4gd2UgbWFrZSBhIGRlc2lnbiBtYXRyaXggZm9yIHRoZSBkaWZmZXJlbnRpYWwgdGVzdCwgYXNzaWduaW5nIHNhbXBsZXMgdG8gYmlvbG9naWNhbCBjb25kaXRpb25zLgoKYGBge3J9CmNvbERhdGEobGl2ZXJfbWlsbylbWydzb3J0J11dIDwtIHN0cl9yZW1vdmUoY29sRGF0YShsaXZlcl9taWxvKVtbJ2RhdGFzZXQnXV0sICIuK18iKQpjb2xEYXRhKGxpdmVyX21pbG8pW1snc29ydCddXSA8LSBzdHJfcmVtb3ZlKGNvbERhdGEobGl2ZXJfbWlsbylbWydzb3J0J11dLCAiQXxCIikKCmxpdmVyX21ldGEgPC0gYXNfdGliYmxlKGNvbERhdGEobGl2ZXJfbWlsbylbLGMoImRhdGFzZXQiLCJjb25kaXRpb24iLCAnc29ydCcpXSkKbGl2ZXJfbWV0YSA8LSBkaXN0aW5jdChsaXZlcl9tZXRhKSAlPiUKICBtdXRhdGUoY29uZGl0aW9uPWZhY3Rvcihjb25kaXRpb24sIGxldmVscz1jKCJVbmluanVyZWQiLCAiQ2lycmhvdGljIikpKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXMoImRhdGFzZXQiKQoKYGBgCgpOb3cgd2UgY2FuIGNvdW50IGNlbGxzIGluIG5laWdoYm91cmhvb2RzIGFuZCBwZXJmb3JtIHRoZSBEQSB0ZXN0LgoKYGBge3J9CmxpdmVyX21pbG8gPC0gY291bnRDZWxscyhsaXZlcl9taWxvLCBzYW1wbGVzID0gImRhdGFzZXQiLCBtZXRhLmRhdGEgPSBkYXRhLmZyYW1lKGNvbERhdGEobGl2ZXJfbWlsbylbLGMoImRhdGFzZXQiLCJjb25kaXRpb24iLCdzb3J0JyldKSApCmxpdmVyX21pbG8gPC0gY2FsY05ob29kRGlzdGFuY2UobGl2ZXJfbWlsbywgZD0xMSkKbWlsb19yZXMgPC0gdGVzdE5ob29kcyhsaXZlcl9taWxvLCBkZXNpZ24gPSB+IGNvbmRpdGlvbiwgZGVzaWduLmRmID0gbGl2ZXJfbWV0YVtjb2xuYW1lcyhuaG9vZENvdW50cyhsaXZlcl9taWxvKSksXSkKbWlsb19yZXNfc29ydCA8LSB0ZXN0Tmhvb2RzKGxpdmVyX21pbG8sIGRlc2lnbiA9IH4gc29ydCArIGNvbmRpdGlvbiwgZGVzaWduLmRmID0gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGl2ZXJfbWV0YVtjb2xuYW1lcyhuaG9vZENvdW50cyhsaXZlcl9taWxvKSksXSkKYGBgCgpgYGB7cn0KY29tcGFyZV9kYV9kZiA8LSBsZWZ0X2pvaW4obWlsb19yZXNfc29ydCwgbWlsb19yZXMsIGJ5PSJOaG9vZCIsIHN1ZmZpeD1jKCJfc29ydCIsICJfbm9zb3J0IikpICU+JQogIHthbm5vdGF0ZU5ob29kcyhsaXZlcl9taWxvLCAuLCAnYW5ub3RhdGlvbl9saW5lYWdlJyl9IAoKY29tcGFyZV9kYV9kZiAlPiUKICBnZ3Bsb3QoYWVzKC1sb2cxMChTcGF0aWFsRkRSX3NvcnQpLCAtbG9nMTAoU3BhdGlhbEZEUl9ub3NvcnQpKSkgKwogIGdlb21fcG9pbnQoc2l6ZT0wLjgpICsKICBnZW9tX3BvaW50KGRhdGE9LiAlPiUgZmlsdGVyKGFubm90YXRpb25fbGluZWFnZT09IkVuZG90aGVsaWEiKSwgY29sb3I9InJlZCIpCnBsb3QobWlsb19yZXNfc29ydCRTcGF0aWFsRkRSLCBtaWxvX3JlcyRTcGF0aWFsRkRSKQpgYGAKCgojIyBFeHBsb3JhdGlvbiBvZiBNaWxvIERBIHJlc3VsdHMKCldlIGNhbiBzdGFydCBieSBsb29raW5nIGF0IHNvbWUgYmFzaWMgc3RhdHMKCmBgYHtyfQpwdmFsX2hpc3QgPC0gbWlsb19yZXMgJT4lCiAgZ2dwbG90KGFlcyhQVmFsdWUpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucz01MCkgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZT0xNCkKCnZvbGNhbm8gPC0KICBtaWxvX3JlcyAlPiUKICBnZ3Bsb3QoYWVzKGxvZ0ZDLCAtbG9nMTAoU3BhdGlhbEZEUikpKSArCiAgZ2VvbV9wb2ludChzaXplPTAuNCwgYWxwaGE9MC4yKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMSkpICsKICB4bGFiKCJsb2ctRm9sZCBDaGFuZ2UiKSArCiAgdGhlbWVfYncoYmFzZV9zaXplPTE0KQoKcHZhbF9oaXN0ICsgdm9sY2FubwpgYGAKClRoZSBkaXN0cmlidXRpb24gb2YgUC12YWx1ZXMgbG9va3Mgc2Vuc2libGUgYW5kIGZyb20gdGhlIHZvbGNhbm8gcGxvdCB3ZSBjYW4gc2VlIHRoYXQgd2UgaGF2ZSBpZGVudGlmaWVkIHNvbWUgREEgbmVpZ2hib3VyaG9vZHMgYXQgMTAlIEZEUi4KCldlIGNhbiB2aXN1YWxpemUgREEgbmVpZ2hib3VyaG9vZHMgYnVpbGRpbmcgYW4gYWJzdHJhY3RlZCBncmFwaAoKYGBge3IsIGZpZy53aWR0aD0xNCwgZmlnLmhlaWdodD0xMH0KbGl2ZXJfbWlsbyA8LSBidWlsZE5ob29kR3JhcGgobGl2ZXJfbWlsbykKcGxvdE5ob29kR3JhcGhEQShsaXZlcl9taWxvLCBtaWxvX3JlcywgYWxwaGEgPSAwLjEsIHNpemVfcmFuZ2U9YygyLDYpKQpgYGAKCiMjIyBOb3RlOiBUaGlzIHN0ZXAgaXMgbm90IHJlcXVpcmVkLiBJdCBnZW5lcmF0ZXMgYSBsYXJnZSBmaWxlIChldmVuIGxhcmdlciB0aGFuIHJhdyBkYXRhKSBhbmQgdGhlbiByZWxvYWRzIGl0LiAKYGBge3Igc2F2ZSBpbnRlcm1lZCBmaWxlcyAyLCBldmFsPUZBTFNFLCBpbmNsdWRlPVRSVUUsIGVjaG89VFJVRX0KIyMgU2F2ZSBtaWxvIG9iamVjdCBhbmQgcmVzdWx0cwojIHNhdmVSRFMobGl2ZXJfbWlsbywifi9kcm9wYm94LXZ1L3RlbXAvbWlsb19kYXRhL1JhbWFjaGFuZHJhbjIwMTlfbGl2ZXIvbGl2ZXJfbWlsb18yMDIxMDIyNS5SRFMiKQojIHdyaXRlX2NzdihtaWxvX3Jlcywifi9kcm9wYm94LXZ1L3RlbXAvbWlsb19kYXRhL1JhbWFjaGFuZHJhbjIwMTlfbGl2ZXIvbGl2ZXJfcmVzdWx0c18yMDIxMDIyNS5jc3YiKQoKYGBgCmBgYHtyIGxvYWQgaW50ZXJtZWQgZmlsZXMgMiwgZXZhbD1GQUxTRSwgaW5jbHVkZT1UUlVFLCBlY2hvPVRSVUV9CiMgbGl2ZXJfbWlsbyA8LSByZWFkUkRTKCJ+L2xpdmVyX21pbG9fMjAyMDEwMDguUkRTIikKbGl2ZXJfbWlsbyA8LSByZWFkUkRTKHVybCgiaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vcy94ZHAwNzc4OWM1aG9lbjMvbGl2ZXJfbWlsb18yMDIxMDIyNS5SRFM/ZGw9MSIpKQojIG1pbG9fcmVzIDwtIHJlYWRfY3N2KCIvbmZzL3RlYW0yMDUvZWQ2L2RhdGEvUmFtYWNoYW5kcmFuMjAxOV9saXZlci9saXZlcl9yZXN1bHRzXzIwMjAxMDA4LmNzdiIpCm1pbG9fcmVzIDwtIHJlYWRfY3N2KHVybCgiaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vcy9pMWwzYWVwMXB5NXdpcmYvbGl2ZXJfcmVzdWx0c18yMDIxMDIyNS5jc3Y/ZGw9MSIpKQpgYGAKCiMjIyBOT1RFOiBgaHZnc2AgKHByZXN1bWFibHkgaGlnaGx5IHZhcmlhYmxlIGdlbmVzKSBjYW5ub3QgYmUgbG9hZGVkIHNpbmNlIGZpbGUgbm90IHByb3ZpZGVkCmBgYHtyfQojIyBMb2FkIGh2Z3MgCiMgaHZncyA8LSBzY2FuKCJ+L2RhdGEvUmFtYWNoYW5kcmFuMjAxOV9saXZlci9saXZlcl9taWxvX2h2Z3MudHh0IiwgIiIpCmBgYAoKIyMjIEF0dGVtcHRpbmcgdG8gZ2VuZXJhdGUgYSBzZXQgb2YgaGlnaGx5IHZhcmlhYmxlIGdlbmVzCmBgYHtyfQojCmBgYAoKTWFraW5nIGZpZ3VyZXMgZm9yIHRoZSBtYW51c2NyaXB0CgpgYGB7ciwgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTEwfQoKY29sb3VyQ291bnQgPSBsZW5ndGgodW5pcXVlKGxpdmVyX21pbG8kYW5ub3RhdGlvbl9saW5lYWdlKSkKZ2V0UGFsZXR0ZSA9IGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbCg5LCAiU2V0MiIpKQoKdW1hcF9kZiA8LSBkYXRhLmZyYW1lKHJlZHVjZWREaW0obGl2ZXJfbWlsbywgIlVNQVAiKSkKY29sbmFtZXModW1hcF9kZikgPC0gYygiVU1BUF8xIiwgIlVNQVBfMiIpCgp1bWFwMSA8LSBjYmluZCh1bWFwX2RmLCBhbm5vdGF0aW9uX2xpbmVhZ2U9bGl2ZXJfbWlsbyRhbm5vdGF0aW9uX2xpbmVhZ2UpICU+JQogIGdncGxvdChhZXMoVU1BUF8xLCBVTUFQXzIsIGNvbG9yPWFzLmNoYXJhY3Rlcihhbm5vdGF0aW9uX2xpbmVhZ2UpKSkgKwogIGdlb21fcG9pbnRfcmFzdChzaXplPTAuMSwgYWxwaGE9MC41LCByYXN0ZXIuZHBpID0gODAwKSArCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGRhdGEgPSAuICU+JQogICAgICAgICAgICAgIGdyb3VwX2J5KGFubm90YXRpb25fbGluZWFnZSkgJT4lCiAgICAgICAgICAgICAgc3VtbWFyaXNlKFVNQVBfMT1tZWFuKFVNQVBfMSksIFVNQVBfMj1tZWFuKFVNQVBfMikpLAogICAgICAgICAgICBhZXMobGFiZWw9YW5ub3RhdGlvbl9saW5lYWdlKSwgY29sb3I9ImJsYWNrIiwgc2l6ZT02CiAgICAgICAgICAgICkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Z2V0UGFsZXR0ZShjb2xvdXJDb3VudCkpICsKICBndWlkZXMoY29sb3I9Im5vbmUiKSArCiAgeGxhYigiVU1BUDEiKSArIHlsYWIoIlVNQVAyIikgKwogIGNvb3JkX2ZpeGVkKCkgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMjIpICsKICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkpCmdnc2F2ZSgifi9kcm9wYm94LXZ1L3RlbXAvbWlsb19vdXRwdXQvbGl2ZXJfdjIvbGl2ZXJfdW1hcDFfbmV3LnBkZiIsIGhlaWdodCA9IDcsIHdpZHRoID0gOCkKCgp1bWFwMiA8LQogIGNiaW5kKHVtYXBfZGYsIGNvbmRpdGlvbj1hcy5jaGFyYWN0ZXIobGl2ZXJfbWlsbyRjb25kaXRpb24pKSAlPiUKICBnZ3Bsb3QoYWVzKFVNQVBfMSwgVU1BUF8yLCBjb2xvcj1jb25kaXRpb24pKSArCiAgZ2VvbV9wb2ludF9yYXN0KHNpemU9MC4xLCBhbHBoYT0wLjUsIHJhc3Rlci5kcGkgPSA4MDApICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iU2V0MSIsIG5hbWU9JycpICsKICB4bGFiKCJVTUFQMSIpICsgeWxhYigiVU1BUDIiKSArCiAgY29vcmRfZml4ZWQoKSArCiAgZ3VpZGVzKGNvbG9yPSdub25lJykgKwogIGZhY2V0X3dyYXAoY29uZGl0aW9ufi4sIG5jb2w9MSkgKwogIHRoZW1lX25vdGhpbmcoZm9udF9zaXplID0gMjIpICsKICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksIGxlZ2VuZC5wb3NpdGlvbj1jKDAuOSwwLjkpLAogICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3I9TkEpLCBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjIpKQpnZ3NhdmUoIn4vZHJvcGJveC12dS90ZW1wL21pbG9fb3V0cHV0L2xpdmVyX3YyL2xpdmVyX3VtYXAyX25ldy5wZGYiLCBoZWlnaHQgPSA3LCB3aWR0aCA9IDgpCgoKbmhfZ3JhcGhfcGwgPC0gcGxvdE5ob29kR3JhcGhEQShsaXZlcl9taWxvLCBtaWxvX3JlcywgYWxwaGEgPSAwLjEsIHNpemVfcmFuZ2U9YygxLDQpKSArCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTIyKSkgKwogIGNvb3JkX2ZpeGVkKCkKCiMgbmhfZ3JhcGhfcGwgKyBnZ3NhdmUoIn4vbW91bnQvZ2RyaXZlL21pbG8vRmlndXJlcy9saXZlcl92Mi9saXZlcl9ncmFwaC5wZGYiLCBoZWlnaHQgPSA3LCB3aWR0aCA9IDgpCiMgZ2dzYXZlKCJ+L2Ryb3Bib3gtdnUvdGVtcC9taWxvX291dHB1dC9saXZlcl92Mi9saXZlcl9ncmFwaC5wZGYiLCBoZWlnaHQgPSA3LCB3aWR0aCA9IDgpCgpnZ3NhdmUoIn4vZHJvcGJveC12dS90ZW1wL21pbG9fb3V0cHV0L2xpdmVyX3YyL2xpdmVyX2dyYXBoX25ldy5wZGYiLCBoZWlnaHQgPSA3LCB3aWR0aCA9IDgpCgpgYGAKCgpgYGB7ciAsIGZpZy53aWR0aD0xNSwgZmlnLmhlaWdodD0xMH0KZmlnNF90b3AgPC0gKHVtYXAxIHwgdW1hcDIgfCBuaF9ncmFwaF9wbCkgKwogIHBsb3RfbGF5b3V0KHdpZHRocyA9IGMoMywxLDMpKQoKZmlnNF90b3AKYGBgCgojIyMgRXhwbG9yZSBEQSBuZWlnaGJvdXJob29kcyBieSBjZWxsIHR5cGUKCk5leHQsIHdlIGNhbiBjaGVjayB0aGUgY2VsbCB0eXBlcyB3aGVyZSB3ZSBvYnNlcnZlIG1vc3QgZGlmZmVyZW5jZXMgYmV0d2VlbiBoZWFsdGh5IGFuZCBjaXJyaG90aWMgY2VsbHMsIGJ5IHRha2luZyB0aGUgbW9zdCBmcmVxdWVudCBjZWxsIHR5cGUgaW4gZWFjaCBuZWlnaGJvdXJob29kLgoKYGBge3IsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTEwfQptaWxvX3JlcyA8LSBtaWxvX3Jlc1ssIXN0cl9kZXRlY3QoY29sbmFtZXMobWlsb19yZXMpLCAiYW5ub3RhdGlvbl9saW5lYWdlIildCgojIEFkZCBhbm5vdGF0aW9uIG9mIG1vc3QgZnJlcXVlbnQgY2VsbCB0eXBlIHBlciBuaG9vZCB0byBtaWxvIHJlc3VsdHMgdGFibGUKbWlsb19yZXMgPC0gYW5ub3RhdGVOaG9vZHMobGl2ZXJfbWlsbywgbWlsb19yZXMsICJhbm5vdGF0aW9uX2luZGVwdGgiKQphbm5vX2RmIDwtIGRhdGEuZnJhbWUobGl2ZXJfbWlsb0Bjb2xEYXRhKSAlPiUKICBkaXN0aW5jdChhbm5vdGF0aW9uX2xpbmVhZ2UsIGFubm90YXRpb25faW5kZXB0aCkKbWlsb19yZXMgPC0gbGVmdF9qb2luKG1pbG9fcmVzLCBhbm5vX2RmLCBieT0iYW5ub3RhdGlvbl9pbmRlcHRoIikKYGBgCgpXZSBmaXJzdCBjaGVjayB0aGF0IG5laWdoYm91cmhvb2RzIGFyZSBzdWZmaWNpZW50bHkgaG9tb2dlbmVvdXMKCmBgYHtyfQpmcmFjX2hpc3QgPC0gZ2dwbG90KG1pbG9fcmVzLCBhZXMoYW5ub3RhdGlvbl9pbmRlcHRoX2ZyYWN0aW9uKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnM9MzApICsKICB4bGFiKCJGcmFjdGlvbiBvZiBjZWxscyBpbiBcbm1vc3QgYWJ1bmRhbnQgY2x1c3RlciIpICsKICB5bGFiKCIjIG5laWdoYm91cmhvb2RzIikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZT0xNCkKCmZyYWNfaGlzdApgYGAKCkZpbHRlciBuaG9vZHMgd2l0aCBob21vZ2VuZW91cyBjb21wb3NpdGlvbgoKYGBge3J9Cm1pbG9fcmVzJGFubm90YXRpb25faW5kZXB0aFttaWxvX3JlcyRhbm5vdGF0aW9uX2luZGVwdGhfZnJhY3Rpb24gPCAwLjZdIDwtIE5BCm1pbG9fcmVzJGFubm90YXRpb25fbGluZWFnZVttaWxvX3JlcyRhbm5vdGF0aW9uX2luZGVwdGhfZnJhY3Rpb24gPCAwLjZdIDwtIE5BCmBgYAoKCkkgY2FuIHJlY292ZXIgYWxsIHRoZSBjbHVzdGVycyB3aGVyZSBEQSB3YXMgZGV0ZWN0ZWQgaW4gdGhlIG9yaWdpbmFsIHBhcGVyCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpncm91cC5ieSA9ICJhbm5vdGF0aW9uX2luZGVwdGgiCnBhcGVyX0RBIDwtIGxpc3QoY2lycmhvdGljPWMoIk1QcyAoNCkiLCJNUHMgKDUpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRW5kb3RoZWxpYSAoNikiLCAiRW5kb3RoZWxpYSAoNykiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNZXMgKDMpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVGNlbGxzICgyKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk15b2ZpYnJvYmxhc3RzIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgaGVhbHRoeT1jKCJNUHMgKDcpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIkVuZG90aGVsaWEgKDEpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRjZWxscyAoMSkiLCAiVGNlbGxzICgzKSIsIlRjZWxscyAoMSkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiSUxDcyAoMSkiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICApCgpleHBEQV9kZiA8LSBiaW5kX3Jvd3MoCiAgZGF0YS5mcmFtZShhbm5vdGF0aW9uX2luZGVwdGggPSBwYXBlcl9EQVtbImNpcnJob3RpYyJdXSwgcHJlZF9EQT0iY2lycmhvdGljIiksCiAgZGF0YS5mcmFtZShhbm5vdGF0aW9uX2luZGVwdGggPSBwYXBlcl9EQVtbImhlYWx0aHkiXV0sIHByZWRfREE9ImhlYWx0aHkiKQogICkKCnBsMSA8LSBtaWxvX3JlcyAlPiUKICBsZWZ0X2pvaW4oZXhwREFfZGYpICU+JQogIG11dGF0ZShpc19zaWduaWYgPSBpZmVsc2UoU3BhdGlhbEZEUiA8IDAuMSwgMSwgMCkpICU+JQogIG11dGF0ZShsb2dGQ19jb2xvciA9IGlmZWxzZShpc19zaWduaWY9PTEsIGxvZ0ZDLCBOQSkpICU+JQogIGFycmFuZ2UoYW5ub3RhdGlvbl9saW5lYWdlKSAlPiUKICBtdXRhdGUoTmhvb2Q9ZmFjdG9yKE5ob29kLCBsZXZlbHM9dW5pcXVlKE5ob29kKSkpICU+JQogIGZpbHRlcighaXMubmEoYW5ub3RhdGlvbl9saW5lYWdlKSkgJT4lCiAgZ2dwbG90KGFlcyhhbm5vdGF0aW9uX2luZGVwdGgsIGxvZ0ZDLCBjb2xvcj1sb2dGQ19jb2xvcikpICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudDIoKSArCiAgZ3VpZGVzKGNvbG9yPSJub25lIikgKwogIHhsYWIoZ3JvdXAuYnkpICsgeWxhYigiTG9nIEZvbGQgQ2hhbmdlIikgKwogIGdnYmVlc3dhcm06Omdlb21fcXVhc2lyYW5kb20oYWxwaGE9MSkgKwogIGNvb3JkX2ZsaXAoKSArCiAgZmFjZXRfZ3JpZChhbm5vdGF0aW9uX2xpbmVhZ2V+Liwgc2NhbGVzPSJmcmVlIiwgc3BhY2U9ImZyZWUiKSArCiAgdGhlbWVfYncoYmFzZV9zaXplPTIyKSArCiAgdGhlbWUoc3RyaXAudGV4dC55ID0gIGVsZW1lbnRfdGV4dChhbmdsZT0wKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgKQoKcGwyIDwtIG1pbG9fcmVzICU+JQogIGxlZnRfam9pbihleHBEQV9kZikgJT4lCiAgIyBkcGx5cjo6ZmlsdGVyKCFpcy5uYShwcmVkX0RBKSkgJT4lCiAgZ3JvdXBfYnkoYW5ub3RhdGlvbl9pbmRlcHRoKSAlPiUKICBzdW1tYXJpc2UocHJlZF9EQT1kcGx5cjo6Zmlyc3QocHJlZF9EQSksIGFubm90YXRpb25fbGluZWFnZT1kcGx5cjo6Zmlyc3QoYW5ub3RhdGlvbl9saW5lYWdlKSkgJT4lCiAgbXV0YXRlKGVuZD1pZmVsc2UocHJlZF9EQT09ImhlYWx0aHkiLCAwLCAxKSwKICAgICAgICAgc3RhcnQ9aWZlbHNlKHByZWRfREE9PSJoZWFsdGh5IiwgMSwgMCkpICU+JQogIGZpbHRlcighaXMubmEoYW5ub3RhdGlvbl9saW5lYWdlKSkgJT4lCiAgZ2dwbG90KGFlcyhhbm5vdGF0aW9uX2luZGVwdGgsIHN0YXJ0LCB4ZW5kID0gYW5ub3RhdGlvbl9pbmRlcHRoLCB5ZW5kID0gZW5kLCBjb2xvcj1wcmVkX0RBKSkgKwogIGdlb21fc2VnbWVudChzaXplPTEsYXJyb3c9YXJyb3cobGVuZ3RoID0gdW5pdCgwLjEsICJucGMiKSwgdHlwZT0iY2xvc2VkIikpICsKICBjb29yZF9mbGlwKCkgKwogIHhsYWIoImFubm90YXRpb24iKSArCiAgZmFjZXRfZ3JpZChhbm5vdGF0aW9uX2xpbmVhZ2V+LiwKICAgICMgYW5ub3RhdGlvbl9saW5lYWdlfiJSYW1hY2hhbmRyYW4gZXQgYWwuXG5EQSBwcmVkaWN0aW9ucyIsCiAgICAgICAgICAgICBzY2FsZXM9ImZyZWUiLCBzcGFjZT0iZnJlZSIpICsKICAjIGd1aWRlcyhjb2xvcj0ibm9uZSIpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iU2V0MSIsIGRpcmVjdGlvbiA9IC0xLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiZW5yaWNoZWQgaW4gY2lycmhvdGljIiwgImVucmljaGVkIGluIGhlYWx0aHkiKSwKICAgICAgICAgICAgICAgICAgICAgbmEudHJhbnNsYXRlID0gRiwKICAgICAgICAgICAgICAgICAgICAgbmFtZT0iUmFtYWNoYW5kcmFuIGV0IGFsLlxuREEgcHJlZGljdGlvbnMiKSArCiAgZ3VpZGVzKGNvbG9yPWd1aWRlX2xlZ2VuZChuY29sID0gMSkpICsKICB0aGVtZV9idyhiYXNlX3NpemU9MjIpICsKICB5bGltKC0wLjEsMS4xKSArCiAgdGhlbWUoc3RyaXAudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCksCiAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMCwwLDAsMCksICJjbSIpLCBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCmZpZzRfYmxlZnQgPC0gKHBsMiArIHBsMSArCiAgcGxvdF9sYXlvdXQod2lkdGhzPWMoMSwxMCksIGd1aWRlcyA9ICJjb2xsZWN0IikgJiB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAndG9wJywgbGVnZW5kLmp1c3RpZmljYXRpb24gPSAwKSkKCiMgZ2dzYXZlKCJ+L21vdW50L2dkcml2ZS9taWxvL0ZpZ3VyZXMvbGl2ZXJfdjIvbGl2ZXJfREFjb21wYXJpc29uLnBkZiIsIHdpZHRoPTgsIGhlaWdodCA9IDEzKQojIGdnc2F2ZSgifi9kcm9wYm94LXZ1L3RlbXAvbWlsb19vdXRwdXQvbGl2ZXJfdjIvbGl2ZXJfREFjb21wYXJpc29uLnBkZiIsIHdpZHRoPTgsIGhlaWdodCA9IDEzKQpgYGAKYGBge3IgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQpwbDIKYGBgCgoKIyMjIENsb3NlLXVwIG9uIEVuZG90aGVsaWFsIGxpbmVhZ2UKCmBgYHtyfQplbmRvX21pbG8gPC0gc2NhdGVyOjpydW5VTUFQKGxpdmVyX21pbG9bLGxpdmVyX21pbG8kYW5ub3RhdGlvbl9saW5lYWdlPT0iRW5kb3RoZWxpYSJdLCAgZGltcmVkPSdQQ0EnKQpwbG90VU1BUChlbmRvX21pbG8sIGNvbG91cl9ieSA9ICJhbm5vdGF0aW9uX2luZGVwdGgiKQpgYGAKCmBgYHtyfQp1bWFwX2RmIDwtIGRhdGEuZnJhbWUocmVkdWNlZERpbShlbmRvX21pbG8sICJVTUFQIikpCmNvbG5hbWVzKHVtYXBfZGYpIDwtIGMoIlVNQVBfMSIsICJVTUFQXzIiKQoKZW5kb191bWFwIDwtIGNiaW5kKHVtYXBfZGYsIGNvbmRpdGlvbj1lbmRvX21pbG8kY29uZGl0aW9uKSAlPiUKICAgZ2dwbG90KGFlcyhVTUFQXzEsIFVNQVBfMiwgY29sb3I9Y29uZGl0aW9uKSkgKwogIGdlb21fcG9pbnQoc2l6ZT0wLjMsIGFscGhhPTAuNSkgKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJTZXQxIiwgbmFtZT0nJykgKwogIHhsYWIoIlVNQVAxIikgKyB5bGFiKCJVTUFQMiIpICsKICBjb29yZF9maXhlZCgpICsKICBndWlkZXMoY29sb3I9Im5vbmUiKSArCiAgZmFjZXRfd3JhcChjb25kaXRpb25+LiwgbmNvbD0xKSArCiAgdGhlbWVfbm90aGluZygpICsKICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksIGxlZ2VuZC5wb3NpdGlvbj1jKDAuOSwwLjkpLAogICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3I9TkEpLCBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjIpKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD00LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaXZlcl9taWxvMiA8LSBsaXZlcl9taWxvCnN1YnNldC5uaG9vZHMgPC0gc3RyX2RldGVjdChtaWxvX3JlcyRhbm5vdGF0aW9uX2luZGVwdGgsICJFbmRvIikKcmVkdWNlZERpbShsaXZlcl9taWxvMiwgIlVNQVAiKVtjb2xuYW1lcyhlbmRvX21pbG8pLF0gPC0gcmVkdWNlZERpbShlbmRvX21pbG8sICJVTUFQIikgCgplbmRvX2dyIDwtCiAgcGxvdE5ob29kR3JhcGhEQSgKICBsaXZlcl9taWxvMiwgbWlsb19yZXMsCiAgc3Vic2V0Lm5ob29kcyA9IHdoaWNoKG1pbG9fcmVzJGFubm90YXRpb25fbGluZWFnZSA9PSAiRW5kb3RoZWxpYSIpLCAKICBzaXplX3JhbmdlPWMoMSw0KSwKICAjICkgPSlbMToobGVuZ3RoKCktMSldLCAKICBhbHBoYSA9IDAuMQogICkgICsKICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTIyKSkKICAKIyBsaXZlcl9taWxvMiA8LSBsaXZlcl9taWxvCiMgc3Vic2V0Lm5ob29kcyA8LSBzdHJfZGV0ZWN0KG1pbG9fcmVzJGFubm90YXRpb25faW5kZXB0aCwgIkVuZG8iKQojIHJlZHVjZWREaW0obGl2ZXJfbWlsbzIsICJVTUFQIilbY29sbmFtZXMoZW5kb19taWxvKSxdIDwtIHJlZHVjZWREaW0oZW5kb19taWxvLCAiVU1BUCIpIAojIGVuZG9fZ3JfZ3JvdXBzIDwtIHBsb3ROaG9vZEdyb3VwcyhsaXZlcl9taWxvMiwgbWlsb19yZXNfZW5kb2dyb3Vwc1ttaWxvX3Jlc19lbmRvZ3JvdXBzJGFubm90YXRpb25fbGluZWFnZT09IkVuZG90aGVsaWEiLF0sIAojICAgICAgICAgICAgICAgICBzaG93X2dyb3VwcyA9IGMoIjU0IiwgIjcwIiksCiMgICAgICAgICAgICAgICAgIHNpemVfcmFuZ2U9YygxLDQpLAojICAgICAgICAgICAgICAgICBzdWJzZXQubmhvb2RzID0gbWlsb19yZXNfZW5kb2dyb3VwcyRhbm5vdGF0aW9uX2xpbmVhZ2U9PSJFbmRvdGhlbGlhIikgKwojICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIjU0Ij1icmV3ZXIucGFsKDQsICJTcGVjdHJhbCIpWzJdLCAiNzAiPWJyZXdlci5wYWwoNCwgIlNwZWN0cmFsIilbM10pLCAKIyAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCI1NCI9IlVuaW5qdXJlZCBncm91cCIsICc3MCc9ICJDaXJyaG90aWMgZ3JvdXAiKSwKIyAgICAgICAgICAgICAgICAgICAgIG5hLnZhbHVlPSJ3aGl0ZSIsCiMgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIk5ob29kIGdyb3VwIgojICAgICAgICAgICAgICAgICAgICAgKSArCiMgICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MjIpKQoKZmlnNF9icmlnaHQxIDwtICgoZW5kb191bWFwICsgZW5kb19nciApICsgCiAgcGxvdF9sYXlvdXQod2lkdGhzID0gYygxLDIpLCAKICAgICAgICAgICAgICAgIGd1aWRlcyA9ICJjb2xsZWN0IgogICAgICAgICAgICAgICAgKSkgCmZpZzRfYnJpZ2h0MQpgYGAKCjwhLS0gYGBge3J9IC0tPgo8IS0tIG5oX2dyYXBoIDwtIG5ob29kR3JhcGgobGl2ZXJfbWlsbylbc3Vic2V0Lm5ob29kcyxzdWJzZXQubmhvb2RzXSAtLT4KPCEtLSBuaF9ncmFwaCA8LSBncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgobmhfZ3JhcGgpIC0tPgoKPCEtLSBjb2xfdmFscyA8LSBjb2xEYXRhKGxpdmVyX21pbG8pW2FzLm51bWVyaWModmVydGV4X2F0dHIobmhfZ3JhcGgpJG5hbWUpLCBjb2xvdXJfYnldIC0tPgo8IS0tIFYobmhfZ3JhcGgpJGNvbG91cl9ieSA8LSBpZmVsc2UobWlsb19yZXNbc3Vic2V0Lm5ob29kcywiU3BhdGlhbEZEUiJdID4gMC4xLCAwLCBtaWxvX3Jlc1tzdWJzZXQubmhvb2RzLCJsb2dGQyJdKSAtLT4KPCEtLSBnZ3JhcGgoc2ltcGxpZnkobmhfZ3JhcGgpKSArIC0tPgo8IS0tICAgICAgIGdlb21fZWRnZV9saW5rMChlZGdlX2NvbG91ciA9ICJncmV5NjYiLCBlZGdlX2FscGhhPTAuMikgICArIC0tPgo8IS0tICAgICAgIGdlb21fbm9kZV9wb2ludChhZXMoZmlsbCA9IGNvbG91cl9ieSksIHNoYXBlPTIxLCBzaXplPTIpICsgLS0+CjwhLS0gICBzY2FsZV9maWxsX2dyYWRpZW50MigpIC0tPgo8IS0tIGBgYCAtLT4KCgojIyMgQ2xvc2UtdXAgb24gQ2hvbGFuZ2lvY3l0ZXMKCmBgYHtyfQpjaG9sX21pbG8gPC0gc2NhdGVyOjpydW5VTUFQKGxpdmVyX21pbG9bLGxpdmVyX21pbG8kYW5ub3RhdGlvbl9saW5lYWdlPT0iQ2hvbGFuZ2lvY3l0ZXMiXSwgIGRpbXJlZD0nUENBJykKcGxvdFVNQVAoY2hvbF9taWxvLCBjb2xvdXJfYnkgPSAiYW5ub3RhdGlvbl9pbmRlcHRoIikKCnBsb3RVTUFQKGNob2xfbWlsbywgY29sb3VyX2J5ID0gInBlcmNlbnQubWl0byIpCmBgYAoKRmlsdGVyIG91dCBjZWxscyB0aGF0IHNob3cgY29udGFtaW5hdGlvbiBmcm9tIGltbXVuZSBjZWxscyAoZXhwcmVzc2lvbiBvZiBpbW11bmUgbWFya2VycykKCmBgYHtyfQprZWVwIDwtIGxvZ2NvdW50cyhjaG9sX21pbG8pWyJDRDE5IixdID09IDAgfCBsb2djb3VudHMoY2hvbF9taWxvKVsiTVM0QTEiLF0gPT0gMApjaG9sX21pbG8gPC0gY2hvbF9taWxvWyxrZWVwXQpjaG9sX21pbG8gPC0gc2NhdGVyOjpydW5VTUFQKGNob2xfbWlsbywgIGRpbXJlZD0nUENBJykKCnBsb3RVTUFQKGNob2xfbWlsbywgY29sb3VyX2J5ID0gImFubm90YXRpb25faW5kZXB0aCIpCmBgYAoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD04fQp1bWFwX2RmIDwtIGRhdGEuZnJhbWUocmVkdWNlZERpbShjaG9sX21pbG8sICJVTUFQIikpCmNvbG5hbWVzKHVtYXBfZGYpIDwtIGMoIlVNQVBfMSIsICJVTUFQXzIiKQoKY2hvbF91bWFwIDwtIGNiaW5kKHVtYXBfZGYsIGNvbmRpdGlvbj1jaG9sX21pbG8kY29uZGl0aW9uKSAlPiUKICAgZ2dwbG90KGFlcyhVTUFQXzEsIFVNQVBfMiwgY29sb3I9Y29uZGl0aW9uKSkgKwogIGdlb21fcG9pbnQoc2l6ZT0wLjMsIGFscGhhPTAuNSkgKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJTZXQxIiwgbmFtZT0nJykgKwogIHhsYWIoIlVNQVAxIikgKyB5bGFiKCJVTUFQMiIpICsKICBjb29yZF9maXhlZCgpICsKICBndWlkZXMoY29sb3I9Im5vbmUiKSArCiAgZmFjZXRfd3JhcChjb25kaXRpb25+LiwgbmNvbD0xKSArCiAgdGhlbWVfbm90aGluZygpICsKICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksIGxlZ2VuZC5wb3NpdGlvbj1jKDAuOSwwLjkpLAogICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3I9TkEpLCBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjIpKQoKY2hvbF91bWFwCmBgYAoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTQsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmxpdmVyX21pbG8yIDwtIGxpdmVyX21pbG8Kc3Vic2V0Lm5ob29kcyA8LSBtaWxvX3JlcyRhbm5vdGF0aW9uX2xpbmVhZ2U9PSJDaG9sYW5naW9jeXRlcyIKcmVkdWNlZERpbShsaXZlcl9taWxvMiwgIlVNQVAiKVtjb2xuYW1lcyhjaG9sX21pbG8pLF0gPC0gcmVkdWNlZERpbShjaG9sX21pbG8sICJVTUFQIikgCgpjaG9sX2dyIDwtCiAgcGxvdE5ob29kR3JhcGhEQSgKICBsaXZlcl9taWxvMiwgbWlsb19yZXMsCiAgc3Vic2V0Lm5ob29kcyA9IHN1YnNldC5uaG9vZHMsCiAgc2l6ZV9yYW5nZT1jKDIsNSksCiAgIyApID0pWzE6KGxlbmd0aCgpLTEpXSwgCiAgYWxwaGEgPSAwLjEKICApICArCiAgIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjIpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0yNCkpCiAgCihjaG9sX3VtYXAgKyBjaG9sX2dyICkgKyAKICBwbG90X2xheW91dCh3aWR0aHMgPSBjKDEsMiksIAogICAgICAgICAgICAgICAgZ3VpZGVzID0gImNvbGxlY3QiCiAgICAgICAgICAgICAgICApCiMgZmlnNF9icmlnaHQxICsKIyAgIGdnc2F2ZSgifi9taWxvX291dHB1dC9saXZlcl9lbmRvR3JhcGgucGRmIiwgd2lkdGg9OSwgaGVpZ2h0ID0gNSkgIAoKIyBnZ3NhdmUoIn4vZHJvcGJveC12dS90ZW1wL21pbG9fb3V0cHV0L2xpdmVyX2VuZG9HcmFwaC5wZGYiLCB3aWR0aD05LCBoZWlnaHQgPSA1KSAgCmBgYAoKIyMjIERpZmZlcmVudGlhbCBHZW5lIEV4cHJlc3Npb24gYW5hbHlzaXMKCkluIGEgc3Vic2V0IG9mIGxpbmVhZ2VzLCB3ZSB3YW50IHRvIHRlc3QgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGJldHdlZW4gbmVpZ2hib3VyaG9vZHMgZW5yaWNoZWQgaW4gY2lycmhvdGljIGNlbGxzIGFuZCBuZWlnaGJvdXJob29kcyBlbnJpY2hlZAoKPCEtLSAoTm93IGNvZGVkIGluIGBtaWxvUlxSXHRlc3REaWZmRXhwLlJgKSAtLT4KCjwhLS0gYGBge3J9IC0tPgo8IS0tIC5wZXJmb3JtX2NvdW50c19kZ2UgPC0gZnVuY3Rpb24oZXhwcnMuZGF0YSwgdGVzdC5tb2RlbCwgZ2VuZS5vZmZzZXQ9Z2VuZS5vZmZzZXQsIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWwuY29udHJhc3RzPU5VTEwsIG4uY29lZj1OVUxMKXsgLS0+Cgo8IS0tICAgICBpLmRnZSA8LSBER0VMaXN0KGNvdW50cz1leHBycy5kYXRhLCAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICBsaWIuc2l6ZT1sb2coY29sU3VtcyhleHBycy5kYXRhKSkpIC0tPgoKPCEtLSAgICAgaWYoaXNUUlVFKGdlbmUub2Zmc2V0KSl7IC0tPgo8IS0tICAgICAgICAgbi5nZW5lIDwtIGFwcGx5KGV4cHJzLmRhdGEsIDIsIGZ1bmN0aW9uKFgpIHN1bShYID4gMCkpIC0tPgo8IS0tICAgICAgICAgaWYobmNvbCh0ZXN0Lm1vZGVsKSA9PSAyKXsgLS0+CjwhLS0gICAgICAgICAgICAgdGVzdC5tb2RlbCA8LSBjYmluZCh0ZXN0Lm1vZGVsLCBuLmdlbmUpIC0tPgo8IS0tICAgICAgICAgICAgIGNvbG5hbWVzKHRlc3QubW9kZWwpIDwtIGMoY29sbmFtZXModGVzdC5tb2RlbClbMToyXSwgIk5HZW5lcyIpIC0tPgo8IS0tICAgICAgICAgfSBlbHNlIGlmIChuY29sKHRlc3QubW9kZWwpID4gMil7IC0tPgo8IS0tICAgICAgICAgICAgIHRlc3QubW9kZWwgPC0gY2JpbmQodGVzdC5tb2RlbFssIDFdLCBuLmdlbmUsIHRlc3QubW9kZWxbLCBjKDI6bmNvbCh0ZXN0Lm1vZGVsKSldKSAtLT4KPCEtLSAgICAgICAgICAgICBjb2xuYW1lcyh0ZXN0Lm1vZGVsKSA8LSBjKGNvbG5hbWVzKHRlc3QubW9kZWwpWzFdLCAiTkdlbmVzIiwgY29sbmFtZXModGVzdC5tb2RlbFssIGMoMjpuY29sKHRlc3QubW9kZWwpKV0pKSAtLT4KPCEtLSAgICAgICAgIH0gZWxzZXsgLS0+CjwhLS0gICAgICAgICAgICAgaWYobmNvbCh0ZXN0Lm1vZGVsKSA8IDIpeyAtLT4KPCEtLSAgICAgICAgICAgICAgICAgd2FybmluZygiT25seSBvbmUgY29sdW1uIGluIG1vZGVsIG1hdHJpeCAtIG11c3QgaGF2ZSBhdCBsZWFzdCAyLiBnZW5lLm9mZnNldCBmb3JjZWQgdG8gIEZBTFNFIikgLS0+CjwhLS0gICAgICAgICAgICAgfSAtLT4KPCEtLSAgICAgICAgIH0gLS0+CjwhLS0gICAgIH0gLS0+Cgo8IS0tICAgICBpLmRnZSA8LSBlc3RpbWF0ZURpc3AoaS5kZ2UsIHRlc3QubW9kZWwpIC0tPgo8IS0tICAgICBpLmZpdCA8LSBnbG1RTEZpdChpLmRnZSwgdGVzdC5tb2RlbCwgcm9idXN0PVRSVUUpIC0tPgoKPCEtLSAgICAgaWYoIWlzLm51bGwobW9kZWwuY29udHJhc3RzKSl7IC0tPgo8IS0tICAgICAgICAgbW9kLmNvbnN0cmFzdCA8LSBtYWtlQ29udHJhc3RzKGNvbnRyYXN0cz1tb2RlbC5jb250cmFzdHMsIGxldmVscz10ZXN0Lm1vZGVsKSAtLT4KPCEtLSAgICAgICAgIGkucmVzIDwtIGFzLmRhdGEuZnJhbWUodG9wVGFncyhnbG1RTEZUZXN0KGkuZml0LCBjb250cmFzdD1tb2QuY29uc3RyYXN0KSwgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29ydC5ieT0nbm9uZScsIG49SW5mKSkgLS0+CjwhLS0gICAgIH0gZWxzZXsgLS0+CjwhLS0gICAgICAgICBpZihpcy5udWxsKG4uY29lZikpeyAtLT4KPCEtLSAgICAgICAgICAgICBuLmNvZWYgPC0gbmNvbCh0ZXN0Lm1vZGVsKSAtLT4KPCEtLSAgICAgICAgIH0gLS0+CjwhLS0gICAgICAgICBpLnJlcyA8LSBhcy5kYXRhLmZyYW1lKHRvcFRhZ3MoZ2xtUUxGVGVzdChpLmZpdCwgY29lZj1uLmNvZWYpLCBzb3J0LmJ5PSdub25lJywgbj1JbmYpKSAtLT4KPCEtLSAgICAgfSAtLT4KPCEtLSAgICAgcmV0dXJuKGkucmVzKSAtLT4KPCEtLSB9IC0tPgoKPCEtLSBgYGAgLS0+CgojIyBOT1RFOiBDb2RlIGJlbG93IGhlcmUgd29uJ3Qgd29yayB1bmxlc3MgYGh2Z3NgIGlzIGRlZmluZWQKIyMjIERSVDogc3RvcHBpbmcgcnVuLXRocm91Z2ggaGVyZQpTaG91bGQgYmUgYWJsZSB0byBydW4gZXZlcnl0aGluZyBhYm92ZS4KLS0tLQoKQWRkIG5ob29kIGV4cHJlc3Npb24gdG8gc3BlZWQtdXAgcGxvdHRpbmcgb2YgaGVhdG1hcHMKCmBgYHtyfQpsaXZlcl9taWxvIDwtIGNhbGNOaG9vZEV4cHJlc3Npb24obGl2ZXJfbWlsbywgYXNzYXkgPSAibG9nY291bnRzIiwgc3Vic2V0LnJvdyA9IGh2Z3MpCmBgYAoKCiMjIEVuZG90aGVsaWEKClJlYnV0dGFsIGZpZ3VyZSBzaG93Y2FzaW5nIGdyb3VwaW5nCgpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTR9CnNldC5zZWVkKDQyKQptaWxvX3Jlc19lbmRvZ3JvdXBzIDwtIGdyb3VwTmhvb2RzKGxpdmVyX21pbG8sIG1pbG9fcmVzLCBtYXgubGZjLmRlbHRhID0gMiwgb3ZlcmxhcCA9IDEpCgpwMSA8LSBwbG90Tmhvb2RHcm91cHMobGl2ZXJfbWlsbywgbWlsb19yZXNfZW5kb2dyb3VwcywgCiAgICAgICAgICAgICAgICBzaXplX3JhbmdlPWMoMSwzKSkgCgptaWxvX3Jlc19lbmRvZ3JvdXBzIDwtIGFubm90YXRlTmhvb2RzKGxpdmVyX21pbG8sIG1pbG9fcmVzX2VuZG9ncm91cHMsICdhbm5vdGF0aW9uX2xpbmVhZ2UnKQoKcDIgPC0gcGxvdERBYmVlc3dhcm0obWlsb19yZXNfZW5kb2dyb3VwcywgZ3JvdXAuYnkgPSAnTmhvb2RHcm91cCcpICsKICBmYWNldF9ncmlkKGFubm90YXRpb25fbGluZWFnZX4uLCBzY2FsZXM9ImZyZWUiLCBzcGFjZT0iZnJlZSIpCgoKIyMgUGxvdCBleHByZXNzaW9uIGluIFQgY2VsbCBuZWlnaGJvdXJob29kcwojIG1hcmtlcnNfZGYgPC0gcmVhZF9jc3YoIn4vbW91bnQvZ2RyaXZlL21pbG8vU1RhYmxlM19SYW1hY2hhbmRyYW4uY3N2IikKdGNlbGxfbWFya2VyX2dlbmVzIDwtIAogIG1hcmtlcnNfZGYgJT4lCiAgZmlsdGVyKGNsdXN0ZXIgJWluJSBjKCJUY2VsbCIsICJJTEMiKSkgJT4lCiAgdG9wX24oMzAsIG15QVVDKSAlPiUKICBwdWxsKGdlbmUpCgpwMyA8LSBwbG90Tmhvb2RFeHByZXNzaW9uR3JvdXBzKGxpdmVyX21pbG8sIG1pbG9fcmVzX2VuZG9ncm91cHMsIGZlYXR1cmVzID0gdW5pcXVlKHRjZWxsX21hcmtlcl9nZW5lcyksIAogICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnNldC5uaG9vZHMgPSBtaWxvX3Jlc19lbmRvZ3JvdXBzJE5ob29kR3JvdXAgJWluJSBjKCIzIiwiMTAiLCAiMTQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZT1UUlVFLCBjbHVzdGVyX2ZlYXR1cmVzID0gVFJVRSxzaG93X3Jvd25hbWVzID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICkgKwogIHRoZW1lKHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCkpCgpgYGAKYGBge3IsIGZpZy53aWR0aD0xNSwgZmlnLmhlaWdodD0xMH0KKCgocDEgKyB0aGVtZSgpKS8gKHAzICsgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTEwLCBhbmdsZT00NSkpKSkgKyAKICBwbG90X2xheW91dChoZWlnaHRzID0gYygxLjEsMSksIGd1aWRlcz0iY29sbGVjdCIKICAgICAgICAgICAgICApfCAKICAoCiAgICBwMiArIHRoZW1lX2J3KGJhc2Vfc2l6ZT0xNikgKyB0aGVtZShzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQoYW5nbGU9MCkpCiAgICApKSArIAogIHBsb3RfbGF5b3V0KHdpZHRocyA9IGMoMS40LCAxKSkgKwogIHBsb3RfYW5ub3RhdGlvbih0YWdfbGV2ZWxzID0gYygiQSIsICJDIiwgIkIiKSApICsKICAjIGdnc2F2ZSgifi9tb3VudC9nZHJpdmUvbWlsby9GaWd1cmVzL2xpdmVyX3YyL1JGaWdfZ3JvdXBpbmcucGRmIiwgd2lkdGg9MTUsIGhlaWdodCA9IDEyKSArCiAgIyBnZ3NhdmUoIn4vbW91bnQvZ2RyaXZlL21pbG8vRmlndXJlcy9saXZlcl92Mi9SRmlnX2dyb3VwaW5nLnBuZyIsIHdpZHRoPTE1LCBoZWlnaHQgPSAxMikKYGBgCgpHcm91cCBlbmRvdGhlbGlhbCBjZWxscyBieSBsb2dGQyBhbmQgREEgcmVzdWx0cwoKYGBge3J9Cm1pbG9fcmVzX2VuZG9ncm91cHMkYW5ub3RhdGlvbl9pbmRlcHRoW21pbG9fcmVzX2VuZG9ncm91cHMkYW5ub3RhdGlvbl9pbmRlcHRoX2ZyYWN0aW9uIDwgMC42XSA8LSBOQQptaWxvX3Jlc19lbmRvZ3JvdXBzJGFubm90YXRpb25fbGluZWFnZVttaWxvX3Jlc19lbmRvZ3JvdXBzJGFubm90YXRpb25faW5kZXB0aF9mcmFjdGlvbiA8IDAuNl0gPC0gTkEKCiMjIEdyb3VwIG5laWdoYm91cmhvb2RzIGJ5IERBIG91dGNvbWUKbWlsb19yZXNfZW5kb2dyb3VwcyROaG9vZEdyb3VwIDwtIE5BCm1pbG9fcmVzX2VuZG9ncm91cHMkTmhvb2RHcm91cCA8LSBpZmVsc2UoKG1pbG9fcmVzX2VuZG9ncm91cHMkYW5ub3RhdGlvbl9saW5lYWdlID09ICJFbmRvdGhlbGlhIikgJiAobWlsb19yZXNfZW5kb2dyb3VwcyRTcGF0aWFsRkRSIDwgMC4xKSAmIChtaWxvX3Jlc19lbmRvZ3JvdXBzJGxvZ0ZDIDwgLTIuNSksICI1NCIsIG1pbG9fcmVzX2VuZG9ncm91cHMkTmhvb2RHcm91cCkKbWlsb19yZXNfZW5kb2dyb3VwcyROaG9vZEdyb3VwIDwtIGlmZWxzZSgobWlsb19yZXNfZW5kb2dyb3VwcyRhbm5vdGF0aW9uX2xpbmVhZ2UgPT0gIkVuZG90aGVsaWEiKSAmIChtaWxvX3Jlc19lbmRvZ3JvdXBzJFNwYXRpYWxGRFIgPCAwLjEpICYgKG1pbG9fcmVzX2VuZG9ncm91cHMkbG9nRkMgPiAyLjUpLCAiNzAiLCBtaWxvX3Jlc19lbmRvZ3JvdXBzJE5ob29kR3JvdXApCgoKbGl2ZXJfbWlsbzIgPC0gbGl2ZXJfbWlsbwpzdWJzZXQubmhvb2RzIDwtIHN0cl9kZXRlY3QobWlsb19yZXMkYW5ub3RhdGlvbl9pbmRlcHRoLCAiRW5kbyIpCnJlZHVjZWREaW0obGl2ZXJfbWlsbzIsICJVTUFQIilbY29sbmFtZXMoZW5kb19taWxvKSxdIDwtIHJlZHVjZWREaW0oZW5kb19taWxvLCAiVU1BUCIpIAplbmRvX2dyX2dyb3VwcyA8LSBwbG90Tmhvb2RHcm91cHMobGl2ZXJfbWlsbzIsIG1pbG9fcmVzX2VuZG9ncm91cHNbbWlsb19yZXNfZW5kb2dyb3VwcyRhbm5vdGF0aW9uX2xpbmVhZ2U9PSJFbmRvdGhlbGlhIixdLCAKICAgICAgICAgICAgICAgIHNob3dfZ3JvdXBzID0gYygiNTQiLCAiNzAiKSwKICAgICAgICAgICAgICAgIHNpemVfcmFuZ2U9YygxLDQpLAogICAgICAgICAgICAgICAgc3Vic2V0Lm5ob29kcyA9IG1pbG9fcmVzX2VuZG9ncm91cHMkYW5ub3RhdGlvbl9saW5lYWdlPT0iRW5kb3RoZWxpYSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiNTQiPWJyZXdlci5wYWwoNCwgIlNwZWN0cmFsIilbMl0sICI3MCI9YnJld2VyLnBhbCg0LCAiU3BlY3RyYWwiKVszXSksIAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCI1NCI9IlVuaW5qdXJlZCBncm91cCIsICc3MCc9ICJDaXJyaG90aWMgZ3JvdXAiKSwKICAgICAgICAgICAgICAgICAgICBuYS52YWx1ZT0id2hpdGUiLAogICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiTmhvb2QgZ3JvdXAiCiAgICAgICAgICAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTIyKSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTE4LCBmaWcuaGVpZ2h0PTV9CmZpZzRfYnJpZ2h0MSA8LSAoKGVuZG9fdW1hcCArIGVuZG9fZ3IpICsgCiAgcGxvdF9sYXlvdXQod2lkdGhzID0gYygxLDIpLCBndWlkZXM9ImNvbGxlY3QiCiAgICAgICAgICAgICAgICApKSAmCiAgdGhlbWUobGVnZW5kLmJveCA9ICJob3Jpem9udGFsIiwgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsIGxlZ2VuZC5kaXJlY3Rpb24gPSAidmVydGljYWwiKQpmaWc0X2JyaWdodDEKYGBgCgoKQ2FsY3VsYXRlIG1hcmtlciBnZW5lcyBiZXR3ZWVuIHRoZSB0d28gZ3JvdXBzCmBgYHtyfQptaXRvX2dlbmVzIDwtIHN0cl9kZXRlY3QoaHZncywgIl5NVC0iKQptYXJrZXJzX2RmIDwtIGZpbmROaG9vZEdyb3VwTWFya2VycyhsaXZlcl9taWxvLCBkYS5yZXMgPSBtaWxvX3Jlc19lbmRvZ3JvdXBzLCBhc3NheT0iY291bnRzIiwKICAgICAgICAgICAgICAgICAgICAgIHN1YnNldC5uaG9vZHMgPSAobWlsb19yZXNfZW5kb2dyb3VwcyROaG9vZEdyb3VwICVpbiUgYygiNTQiLCAiNzAiKSksCiAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQuZ3JvdXBzID0gYygiNTQiLCAiNzAiKSwKICAgICAgICAgICAgICAgICAgICAgIHN1YnNldC5yb3cgPSBodmdzWyFtaXRvX2dlbmVzXSwKICAgICAgICAgICAgICAgICAgICAgIGFnZ3JlZ2F0ZS5zYW1wbGVzID0gVFJVRSwgc2FtcGxlX2NvbCA9ICJkYXRhc2V0IgogICAgICAgICAgICAgICAgICAgICAgKQoKbWlsb19yZXNfZW5kb2dyb3Vwc1ttaWxvX3Jlc19lbmRvZ3JvdXBzJE5ob29kR3JvdXAgJWluJSBjKCI1NCIsICI3MCIpLF0KCmNvbG5hbWVzKG1hcmtlcnNfZGYpIDwtIHN0cl9yZXBsYWNlKGNvbG5hbWVzKG1hcmtlcnNfZGYpLCAiNzAiLCAiY2lyciIpCmNvbG5hbWVzKG1hcmtlcnNfZGYpIDwtIHN0cl9yZXBsYWNlKGNvbG5hbWVzKG1hcmtlcnNfZGYpLCAiNTQiLCAidW5pbmoiKQpgYGAKCiMjIyMgVmlzdWFsaXplIGFzIHZvbGNhbm8gCgpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgpoaWdobGlnaHRfZ2VuZXMgPC0gYygiUExWQVAiLCAiVldBMSIsICJBQ0tSMSIsICJJTDMyIiwKICAgICAgICAgICAgICAgICAgICAgIkNMRUM0RyIsICJDTEVDNE0iLCAiRkNOMiIsICJGQ04zIiwKICAgICAgICAgICAgICAgICAgICAgIkxFRjEiKQoKbWFya2VyLmRmIDwtIG1hcmtlcnNfZGYKbWFya2VyLmRmICU+JQogIG11dGF0ZShsYWJlbD1pZmVsc2UoR2VuZUlEICVpbiUgaGlnaGxpZ2h0X2dlbmVzLCBHZW5lSUQsIE5BKSkgJT4lCiAgZ2dwbG90KGFlcyhsb2dGQ19jaXJyLCAtbG9nMTAoYWRqLlAuVmFsX2NpcnIpLCAKICAgICAgICAgICAgICMgY29sb3I9aGlnaGxpZ2h0CiAgICAgICAgICAgICApKSArIAogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1sYWJlbCksIGNvbG9yPSJyZWQiKSArCiAgeGxhYigibG9nRkMiKSArIHlsYWIoIi0gbG9nMTAoQWRqLiBQIHZhbHVlKSIpICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSAyMikKICAKYGBgCgoKIyMjIyBWaXN1YWxpemUgYXMgaGVhdG1hcCAKKGdlbmUgZXhwcmVzc2lvbiB2YWx1ZXMgYXJlIHNjYWxlZCBiZXR3ZWVuIDAgYW5kIDEgZm9yIGVhY2ggZ2VuZSkKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTIsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cm1hcmtlcl9nZW5lcyA8LSBtYXJrZXIuZGYgJT4lCiAgZHBseXI6OmZpbHRlcihhZGouUC5WYWxfY2lyciA8IDAuMDUpICU+JQogIHB1bGwoR2VuZUlEKQoKZmlnNF9iYnJpZ2h0IDwtCiAgcGxvdE5ob29kRXhwcmVzc2lvbkRBKGxpdmVyX21pbG8sIG1pbG9fcmVzX2VuZG9ncm91cHMsIGMobWFya2VyX2dlbmVzKSwgY2x1c3Rlcl9mZWF0dXJlcyA9IFRSVUUsIGFzc2F5ID0gImNvdW50cyIsCiAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuMSwKICAgICAgICAgICAgICAgICAgICAgIHNjYWxlX3RvXzEgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgc3Vic2V0Lm5ob29kcyA9ICBtaWxvX3Jlc19lbmRvZ3JvdXBzJE5ob29kR3JvdXAgJWluJSBjKCI1NCIsICI3MCIpLAogICAgICAgICAgICAgICAgICAgICAgIyBncmlkLnNwYWNlID0gImZyZWUiLAogICAgICAgICAgICAgICAgICAgICAgaGlnaGxpZ2h0X2ZlYXR1cmVzID0gaGlnaGxpZ2h0X2dlbmVzLCBzaG93X3Jvd25hbWVzID0gRkFMU0UKICAgICAgICAgICAgICAgICAgICAgICkgKwogIHlsYWIoIkRFIGdlbmVzIikrCiAgIyBmYWNldF9ncmlkKC5+Tmhvb2RHcm91cCwgc2NhbGVzPSJmcmVlIiwgc3BhY2U9ImZyZWUiKQogICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIyKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MjQpKSArCiAgcGxvdF9sYXlvdXQoaGVpZ2h0cyA9IGMoMSwxMCkpICYgdGhlbWUobGVnZW5kLm1hcmdpbiA9IG1hcmdpbigwLDAsMCw2MCksIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKQoKICAKcGwzIDwtIGZpZzRfYmJyaWdodCRkYXRhICU+JQogIGdncGxvdChhZXMobG9nRkNfcmFuaywgMSxmaWxsPWxvZ0ZDKSkgKwogIGdlb21fdGlsZSgpICsKICAgICAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemU9MTYpICsKICAgIHlsYWIoIiIpICsKICBzY2FsZV9maWxsX2dyYWRpZW50MihuYW1lPSJEQSBsb2dGQyIpICsKICAgICMgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIjU0Ij1icmV3ZXIucGFsKDQsICJTcGVjdHJhbCIpWzJdLCAiNzAiPWJyZXdlci5wYWwoNCwgIlNwZWN0cmFsIilbM10pLCAKICAgICMgICAgICAgICAgICAgICAgIGxhYmVscz1jKCI1NCI9IlVuaW5qdXJlZCBncm91cCIsICc3MCc9ICJDaXJyaG90aWMgZ3JvdXAiKSwKICAgICMgICAgICAgICAgICAgICAgIG5hLnZhbHVlPSJ3aGl0ZSIsCiAgICAjICAgICAgICAgICAgICAgICBuYW1lID0gIk5ob29kIGdyb3VwIgogICAgIyAgICAgICAgICAgICAgICAgKSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLjAxLCAwKSkgKwogICAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLCBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKCmZpZzRfYmJyaWdodCA8LSBwbDMgLyBmaWc0X2JicmlnaHQgICsKICBwbG90X2xheW91dChoZWlnaHRzID0gYygxLDIwKSkKCmZpZzRfYmJyaWdodApgYGAKCiMjIyBHTyB0ZXJtIGFuYWx5c2lzCgpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyBCaW9jTWFuYWdlcjo6aW5zdGFsbCgnY2x1c3RlclByb2ZpbGVyJykKIyBCaW9jTWFuYWdlcjo6aW5zdGFsbCgnbXNpZ2RicicpCmxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKQpsaWJyYXJ5KG1zaWdkYnIpCgptX2RmIDwtIG1zaWdkYnIoc3BlY2llcyA9ICJIb21vIHNhcGllbnMiKQptX3QyZyA8LSBtc2lnZGJyKHNwZWNpZXMgPSAiSG9tbyBzYXBpZW5zIiwgY2F0ZWdvcnkgPSAiQzUiLCBzdWJjYXRlZ29yeSA9ICJCUCIpICAlPiUgCiAgZHBseXI6OnNlbGVjdChnc19uYW1lLCBnZW5lX3N5bWJvbCkKYGBgCgpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KbWFya2VyX2dlbmVzX3VwIDwtIG1hcmtlci5kZiAlPiUKICBkcGx5cjo6ZmlsdGVyKGFkai5QLlZhbF9jaXJyIDwgMC4wNSAmIGxvZ0ZDX2NpcnIgPiAwLjUpICU+JQogIHB1bGwoR2VuZUlEKSAKCm1hcmtlcl9nZW5lc19kb3duIDwtIG1hcmtlci5kZiAlPiUKICBkcGx5cjo6ZmlsdGVyKGFkai5QLlZhbF9jaXJyIDwgMC4wNSAmIGxvZ0ZDX3VuaW5qID4gMC41KSAlPiUKICBwdWxsKEdlbmVJRCkKCmVtX3VwIDwtIGVucmljaGVyKG1hcmtlcl9nZW5lc191cCwgVEVSTTJHRU5FPW1fdDJnLCBwQWRqdXN0TWV0aG9kID0gImZkciIsIAogICAgICAgICAgICAgICAgICB1bml2ZXJzZSA9IGh2Z3MKICAgICAgICAgICAgICAgICAgKQplbV9kb3duIDwtIGVucmljaGVyKG1hcmtlcl9nZW5lc19kb3duLCBURVJNMkdFTkU9bV90MmcsIHBBZGp1c3RNZXRob2QgPSAiZmRyIiwgCiAgICAgICAgICAgICAgICAgICAgdW5pdmVyc2UgPSByb3duYW1lcyhsaXZlcl9taWxvKQogICAgICAgICAgICAgICAgICAgICkKCmVtX3Jlc191cCA8LSBlbV91cEByZXN1bHRbZW1fdXBAcmVzdWx0JHF2YWx1ZSA8IDAuMSxdICU+JQogIGRwbHlyOjpzZWxlY3QoLSBjKERlc2NyaXB0aW9uKSkKZW1fcmVzX2Rvd24gPC0gZW1fZG93bkByZXN1bHRbZW1fZG93bkByZXN1bHQkcXZhbHVlIDwgMC4xLF0gJT4lCiAgZHBseXI6OnNlbGVjdCgtIGMoRGVzY3JpcHRpb24pKQpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xNSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KZ29fZW5kb191cCA8LSBlbV9yZXNfdXAgJT4lCiAgdG9wX24oMzAsIC1sb2cxMChxdmFsdWUpKSAlPiUKICAgbXV0YXRlKElEPWlmZWxzZShJRD09J0dPX0FOVElHRU5fUFJPQ0VTU0lOR19BTkRfUFJFU0VOVEFUSU9OX09GX1BFUFRJREVfT1JfUE9MWVNBQ0NIQVJJREVfQU5USUdFTl9WSUFfTUhDX0NMQVNTX0lJJywgIkdPX0FOVElHRU5fUFJFU0VOVEFUSU9OX1ZJQV9NSENfQ0xBU1NfSUkiLCBJRCkpICU+JQogIG11dGF0ZShUZXJtPWZhY3RvcihJRCwgbGV2ZWxzPXJldih1bmlxdWUoSUQpKSkpICU+JQogIGdncGxvdChhZXMoVGVybSwgLWxvZzEwKHF2YWx1ZSkpKSArCiAgZ2VvbV9wb2ludCgpICsKICBjb29yZF9mbGlwKCkgKwogIHhsYWIoIkdPIEJpb2xvZ2ljYWwgRnVuY3Rpb24iKSArIHlsYWIoIi1sb2cxMChBZGouIHAtdmFsdWUpIikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZT0xOCkgKwogIGdndGl0bGUoIkNpcnJob3RpYyBlbmRvdGhlbGlhIikKCmdvX2VuZG9fZG93biA8LSBlbV9yZXNfZG93biAlPiUKICB0b3BfbigzMCwgLWxvZzEwKHF2YWx1ZSkpICU+JQogIG11dGF0ZShJRD1pZmVsc2UoSUQ9PSdHT19BTlRJR0VOX1BST0NFU1NJTkdfQU5EX1BSRVNFTlRBVElPTl9PRl9QRVBUSURFX09SX1BPTFlTQUNDSEFSSURFX0FOVElHRU5fVklBX01IQ19DTEFTU19JSScsICJHT19BTlRJR0VOX1BSRVNFTlRBVElPTl9WSUFfTUhDX0NMQVNTX0lJIiwgSUQpKSAlPiUKICBtdXRhdGUoVGVybT1mYWN0b3IoSUQsIGxldmVscz1yZXYodW5pcXVlKElEKSkpKSAlPiUKICBnZ3Bsb3QoYWVzKFRlcm0sIC1sb2cxMChxdmFsdWUpKSkgKwogIGdlb21fcG9pbnQoKSArCiAgY29vcmRfZmxpcCgpICsKICB4bGFiKCJHTyBCaW9sb2dpY2FsIEZ1bmN0aW9uIikgKyB5bGFiKCItbG9nMTAoQWRqLiBwLXZhbHVlKSIpICsKICB0aGVtZV9idyhiYXNlX3NpemU9MTgpICsKICBnZ3RpdGxlKCJVbmluanVyZWQgZW5kb3RoZWxpYSIpCgpnb19lbmRvX3VwCmdvX2VuZG9fZG93bgpgYGAKCgpgYGB7cn0KZW1fcmVzX3VwCmVtX3Jlc19kb3duCmBgYAoKIyMgQ2hvbGFuZ2lvY3l0ZXMKCmBgYHtyfQpzZXQuc2VlZCg0MikKbWlsb19yZXNfY2hvbGdyb3VwcyA8LSBncm91cE5ob29kcyhsaXZlcl9taWxvLCBtaWxvX3JlcywgbWF4LmxmYy5kZWx0YSA9IDAuNSwgb3ZlcmxhcCA9IDEpCgojIyBHcm91cCBuZWlnaGJvdXJob29kcyBieSBEQSBvdXRjb21lCm1pbG9fcmVzX2Nob2xncm91cHMkTmhvb2RHcm91cCA8LSBOQQptaWxvX3Jlc19jaG9sZ3JvdXBzJE5ob29kR3JvdXAgPC0gaWZlbHNlKChtaWxvX3Jlc19jaG9sZ3JvdXBzJGFubm90YXRpb25fbGluZWFnZSA9PSAiQ2hvbGFuZ2lvY3l0ZXMiKSAmIChtaWxvX3Jlc19jaG9sZ3JvdXBzJFNwYXRpYWxGRFIgPCAwLjEpICYgKG1pbG9fcmVzX2Nob2xncm91cHMkbG9nRkMgPCAtMi41KSwgIjM4IiwgbWlsb19yZXNfY2hvbGdyb3VwcyROaG9vZEdyb3VwKQptaWxvX3Jlc19jaG9sZ3JvdXBzJE5ob29kR3JvdXAgPC0gaWZlbHNlKChtaWxvX3Jlc19jaG9sZ3JvdXBzJGFubm90YXRpb25fbGluZWFnZSA9PSAiQ2hvbGFuZ2lvY3l0ZXMiKSAmIChtaWxvX3Jlc19jaG9sZ3JvdXBzJFNwYXRpYWxGRFIgPCAwLjEpICYgKG1pbG9fcmVzX2Nob2xncm91cHMkbG9nRkMgPiAyLjUpLCAiNDkiLCBtaWxvX3Jlc19jaG9sZ3JvdXBzJE5ob29kR3JvdXApCgpsaXZlcl9taWxvMiA8LSBsaXZlcl9taWxvCnN1YnNldC5uaG9vZHMgPC0gc3RyX2RldGVjdChtaWxvX3JlcyRhbm5vdGF0aW9uX2luZGVwdGgsICJDaG9sIikKcmVkdWNlZERpbShsaXZlcl9taWxvMiwgIlVNQVAiKVtjb2xuYW1lcyhjaG9sX21pbG8pLF0gPC0gcmVkdWNlZERpbShjaG9sX21pbG8sICJVTUFQIikgCnBsb3ROaG9vZEdyb3VwcyhsaXZlcl9taWxvMiwgbWlsb19yZXNfY2hvbGdyb3Vwc1ttaWxvX3Jlc19jaG9sZ3JvdXBzJGFubm90YXRpb25fbGluZWFnZT09IkNob2xhbmdpb2N5dGVzIixdLCAKICAgICAgICAgICAgICAgIHNob3dfZ3JvdXBzID0gYygiNDkiLCIzOCIpLAogICAgICAgICAgICAgICAgc3Vic2V0Lm5ob29kcyA9ICBtaWxvX3Jlc19jaG9sZ3JvdXBzJGFubm90YXRpb25fbGluZWFnZSA9PSJDaG9sYW5naW9jeXRlcyIpCgpgYGAKCkNhbGN1bGF0ZSBtYXJrZXIgZ2VuZXMgYmV0d2VlbiB0aGUgdHdvIGdyb3VwcwpgYGB7cn0KIyMgRmlsdGVyIGdlbmVzIGV4cHJlc3NlZCBpbiBjaG9sYW5naW9jeXRlcwojIGNob2xfaHZncyA8LSBodmdzWyhjb3VudHMoY2hvbF9taWxvKVtodmdzLF0gPiAwKSAlPiUge3Jvd1N1bXMoLikvbmNvbChjaG9sX21pbG8pfSA+IDAuMDFdCm1pdG9fZ2VuZXMgPC0gc3RyX2RldGVjdChodmdzLCAiXk1ULSIpCgptYXJrZXJzX2RmIDwtIGZpbmROaG9vZEdyb3VwTWFya2VycyhsaXZlcl9taWxvLCBkYS5yZXMgPSBtaWxvX3Jlc19jaG9sZ3JvdXBzLCBhc3NheT0iY291bnRzIiwKICAgICAgICAgICAgICAgICAgICAgIHN1YnNldC5uaG9vZHMgPSBtaWxvX3Jlc19jaG9sZ3JvdXBzJE5ob29kR3JvdXAgJWluJWMoIjQ5IiwiMzgiKSwKICAgICAgICAgICAgICAgICAgICAgIHN1YnNldC5ncm91cHMgPSBjKCI0OSIsIjM4IiksCiAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQucm93ID0gaHZnc1shbWl0b19nZW5lc10sCiAgICAgICAgICAgICAgICAgICAgICBhZ2dyZWdhdGUuc2FtcGxlcyA9IFRSVUUsIHNhbXBsZV9jb2wgPSAiZGF0YXNldCIKICAgICAgICAgICAgICAgICAgICAgICkKCm1hcmtlcnNfZGYgCgptaWxvX3Jlc19jaG9sZ3JvdXBzW21pbG9fcmVzX2Nob2xncm91cHMkTmhvb2RHcm91cCAlaW4lYygiNDkiLCIzOCIpLF0KYGBgCgojIyMjIFZpc3VhbGl6ZSBhcyB2b2xjYW5vIAoKYGBge3IsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEwLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQptYXJrZXIuZGYuY2hvbCA8LSBtYXJrZXJzX2RmCgp2b2xjYW5vX2Nob2wgPC0KICBtYXJrZXIuZGYuY2hvbCAlPiUKICBtdXRhdGUodXA9aWZlbHNlKGxvZ0ZDXzQ5ID4gMCwgInVwIiwgImRvd24iKSkgJT4lCiAgZ3JvdXBfYnkodXApICU+JQogIG11dGF0ZShsYWJlbD1pZmVsc2UocmFuayhhZGouUC5WYWxfNDkpIDwgMTUsIEdlbmVJRCwgTkEpKSAlPiUKICAjIG11dGF0ZShsYWJlbD1pZmVsc2UoKGFkai5QLlZhbF80OSA8IDAuMDUgJiBsb2dGQ180OSA8IC0zKSB8IChhZGouUC5WYWxfNDkgPCAwLjA1ICYgbG9nRkNfNDkgPiAwKSwgR2VuZUlELCBOQSkpICU+JQogIGdncGxvdChhZXMobG9nRkNfNDksIC1sb2cxMChhZGouUC5WYWxfNDkpLCAKICAgICAgICAgICAgICMgY29sb3I9aGlnaGxpZ2h0CiAgICAgICAgICAgICApKSArIAogIGdlb21fcG9pbnQoc2l6ZT0wLjgsIGFscGhhPTAuNikgKwogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChhZXMobGFiZWw9bGFiZWwpLCBzZWdtZW50LmFscGhhID0gMC4yKSArCiAgeGxhYigibG9nRkMiKSArIHlsYWIoIi0gbG9nMTAoQWRqLiBQIHZhbHVlKSIpICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSAyMikKCnZvbGNhbm9fY2hvbCAgCiAgCmBgYAoKCgojIyMgR08gdGVybSBhbmFseXNpcwoKYGBge3IsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9Cm1hcmtlcl9nZW5lc19jaG9sIDwtIG1hcmtlci5kZi5jaG9sICU+JQogIGRwbHlyOjpmaWx0ZXIoYWRqLlAuVmFsXzQ5IDwgMC4wNSAmIGxvZ0ZDXzQ5ID4gMCkgJT4lCiAgcHVsbChHZW5lSUQpCgplbV91cF9jaG9sIDwtIGVucmljaGVyKG1hcmtlcl9nZW5lc19jaG9sLCBURVJNMkdFTkU9bV90MmcsIHBBZGp1c3RNZXRob2QgPSAiZmRyIiwgCiAgICAgICAgICAgICAgICAgIHVuaXZlcnNlID0gcm93bmFtZXMobGl2ZXJfbWlsbykKICAgICAgICAgICAgICAgICAgKQoKZW1fcmVzX3VwX2Nob2wgPC0gZW1fdXBfY2hvbEByZXN1bHRbZW1fdXBfY2hvbEByZXN1bHQkcXZhbHVlIDwgMC4xLF0gJT4lCiAgZHBseXI6OnNlbGVjdCgtIGMoRGVzY3JpcHRpb24pKQpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xNSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KZ29fY2hvbF91cCA8LSBlbV9yZXNfdXBfY2hvbCAlPiUKICB0b3BfbigyMCwgLWxvZzEwKHF2YWx1ZSkpICU+JQogIG11dGF0ZShUZXJtPWZhY3RvcihJRCwgbGV2ZWxzPXJldih1bmlxdWUoSUQpKSkpICU+JQogIGdncGxvdChhZXMoVGVybSwgLWxvZzEwKHF2YWx1ZSkpKSArCiAgZ2VvbV9wb2ludCgpICsKICBjb29yZF9mbGlwKCkgKwogIHhsYWIoIkdPIEJpb2xvZ2ljYWwgRnVuY3Rpb24iKSArIHlsYWIoIi1sb2cxMChBZGouIHAtdmFsdWUpIikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZT0xOCkgKwogIGdndGl0bGUoIkNpcnJob3RpYyBjaG9sYW5naW9jeXRlcyIpCgpnb19jaG9sX3VwCmBgYAoKYGBge3J9CmVtX3Jlc191cF9jaG9sCmBgYApgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KbWFya2VyX2dlbmVzX2Nob2xfZG93biA8LSBtYXJrZXIuZGYuY2hvbCAlPiUKICBkcGx5cjo6ZmlsdGVyKGFkai5QLlZhbF80OSA8IDAuMDUgJiBsb2dGQ180OSA8IDApICU+JQogIHB1bGwoR2VuZUlEKQoKZW1fZG93bl9jaG9sIDwtIGVucmljaGVyKG1hcmtlcl9nZW5lc19jaG9sX2Rvd24sIFRFUk0yR0VORT1tX3QyZywgcEFkanVzdE1ldGhvZCA9ICJmZHIiLCAKICAgICAgICAgICAgICAgICAgdW5pdmVyc2UgPSByb3duYW1lcyhsaXZlcl9taWxvKQogICAgICAgICAgICAgICAgICApCgplbV9yZXNfZG93bl9jaG9sIDwtIGVtX2Rvd25fY2hvbEByZXN1bHRbZW1fZG93bl9jaG9sQHJlc3VsdCRxdmFsdWUgPCAwLjEsXSAlPiUKICBkcGx5cjo6c2VsZWN0KC0gYyhEZXNjcmlwdGlvbikpCmBgYAoKCi0tLQoKQXNzZW1ibGUgZmlndXJlCmBgYHtyLCBmaWcuaGVpZ2h0PTI1LCBmaWcud2lkdGg9MTl9CmZpZzRfYm90dG9tIDwtICgoZmlnNF9ibGVmdCArIHBsb3RfbGF5b3V0KCkpIHwKICAgICAgKChmaWc0X2JyaWdodDEgKyBwbG90X2xheW91dCh0YWdfbGV2ZWwgPSAna2VlcCcpKSAvIChmaWc0X2JicmlnaHQgKyBwbG90X2xheW91dCgpKSkgKwogICAgICBwbG90X2xheW91dChoZWlnaHRzID0gYygxLDEuNikpCiAgICkgKwogIHBsb3RfbGF5b3V0KHdpZHRocz1jKDEsMS40KSkKCihmaWc0X3RvcCAvIGZpZzRfYm90dG9tKSArCiAgcGxvdF9sYXlvdXQoaGVpZ2h0cz1jKDEsMS44KSkgICsKICBnZ3NhdmUoIn4vbW91bnQvZ2RyaXZlL21pbG8vRmlndXJlcy9saXZlcl92Mi9maWc0X3Jhdy5wZGYiLCBoZWlnaHQgPSAyNiwgd2lkdGggPSAyNCwgdXNlRGluZ2JhdHM9RkFMU0UpIAogICMgZ2dzYXZlKCJ+L21vdW50L2dkcml2ZS9taWxvL0ZpZ3VyZXMvbGl2ZXJfdjIvZmlnNF9yYXcucG5nIiwgaGVpZ2h0ID0gMjYsIHdpZHRoID0gMjQsIHVzZURpbmdiYXRzPUZBTFNFKQogICMgZ2dzYXZlKCJ+L21pbG8vbXMvZmlndXJlcy9maWdzL2ZpZ3VyZTUucGRmIiwgaGVpZ2h0ID0gMjYsIHdpZHRoID0gMjIsIHVzZURpbmdiYXRzPUZBTFNFKQpgYGAKCkFzc2VtYmxlIHN1cHBsZW1lbnRhcnkgZmlndXJlCgpgYGB7ciwgZmlnLndpZHRoPTI1LCBmaWcuaGVpZ2h0PTd9CnAxIDwtIHBsb3RfZ3JpZCggZ29fZW5kb191cCsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEpKSwgCiAgICAgICAgICAgICAgICAgZ29fZW5kb19kb3duKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoaGp1c3QgPSAxKSksIAogICAgICAgICAgICAgICAgIGxhYmVsX3NpemUgPSAxOCwKICAgICAgICAgICAgICAgICBuY29sPTEsCiAgICAgICAgICAgICAgICAgcmVsX2hlaWdodHMgPSBjKDIsMiksCiAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJBIiwgIkIiLCJDIikpCgpwMQoKY2hvbF9lbWIgPC0gKGNob2xfdW1hcCArIGNob2xfZ3IgKSArIAogIHBsb3RfbGF5b3V0KHdpZHRocyA9IGMoMSwyKSwgCiAgICAgICAgICAgICAgICBndWlkZXMgPSAiY29sbGVjdCIKICAgICAgICAgICAgICAgICkKCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD04fQpwbG90X2dyaWQoCiAgZ29fZW5kb191cCsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEpKSwgCiAgICAgICAgICAgICAgICAgZ29fZW5kb19kb3duKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoaGp1c3QgPSAxKSksIAogICAgICAgICAgICAgICAgIGxhYmVsX3NpemUgPSAxOCwKICAgICAgICAgICAgICAgICBuY29sPTEsCiAgICAgICAgICAgICAgICAgcmVsX2hlaWdodHMgPSBjKDIsMiksIHJlbF93aWR0aHMgPSBjKDIsMiksCiAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJBIiwgIkIiKQogICkgKwogIGdnc2F2ZSgifi9tb3VudC9nZHJpdmUvbWlsby9GaWd1cmVzL2xpdmVyX3YyL3N1cHBsX2ZpZ19lbmRvLnBkZiIsIGhlaWdodCA9IDEyLCB3aWR0aD0xMikgKwogIGdnc2F2ZSgifi9tb3VudC9nZHJpdmUvbWlsby9GaWd1cmVzL2xpdmVyX3YyL3N1cHBsX2ZpZ19lbmRvLnBuZyIsIGhlaWdodCA9IDEyLCB3aWR0aD0xMikKCgpgYGAKYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD04fQpwbG90X2dyaWQocGxvdF9ncmlkKGNob2xfdW1hcCwgY2hvbF9nciwgdm9sY2Fub19jaG9sLCBucm93PTEscmVsX3dpZHRocyA9IGMoMSwyLDIpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsX3NpemUgPSAxOCwKICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkEiLCJCIiwiQyIpKSwKICAgICAgICAgICAgICAgIGdvX2Nob2xfdXAgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSkpLCAKICAgICAgICAgICAgICAgIG5jb2w9MSwKICAgICAgICAgICAgICAgIHJlbF9oZWlnaHRzID0gYygxLDEpLAogICAgICAgICAgICAgICAgIGxhYmVsX3NpemUgPSAxOCwKICAgICAgICAgICAgICAgIGxhYmVscz1jKCIiLCdEJykpICsKICAgZ2dzYXZlKCJ+L21vdW50L2dkcml2ZS9taWxvL0ZpZ3VyZXMvbGl2ZXJfdjIvc3VwcGxfZmlnNy5wZGYiLCBoZWlnaHQgPSAxMywgd2lkdGg9MTQpICsKICBnZ3NhdmUoIn4vbW91bnQvZ2RyaXZlL21pbG8vRmlndXJlcy9saXZlcl92Mi9zdXBwbF9maWc3LnBuZyIsIGhlaWdodCA9IDEzLCB3aWR0aD0xNCkgCmBgYAo=